From 618a18c998778bf871b905a74657440fcc384c80 Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Thu, 2 Jul 2020 22:10:22 +0200 Subject: [PATCH] Adding shadows, greedy meshing, and more. --- Cargo.lock | 41 +- assets/common/npc_names.json | 28 + assets/voxygen/shaders/figure-frag.glsl | 126 +- assets/voxygen/shaders/figure-vert.glsl | 100 +- assets/voxygen/shaders/fluid-frag/cheap.glsl | 136 +- assets/voxygen/shaders/fluid-frag/shiny.glsl | 79 +- assets/voxygen/shaders/fluid-vert.glsl | 61 +- assets/voxygen/shaders/include/constants.glsl | 13 + assets/voxygen/shaders/include/globals.glsl | 3 + assets/voxygen/shaders/include/light.glsl | 151 +- assets/voxygen/shaders/include/lod.glsl | 28 +- assets/voxygen/shaders/include/sky.glsl | 464 ++- assets/voxygen/shaders/include/srgb.glsl | 162 +- .../shaders/light-shadows-directed-frag.glsl | 48 + .../shaders/light-shadows-directed-vert.glsl | 63 + .../shaders/light-shadows-figure-vert.glsl | 74 + .../voxygen/shaders/light-shadows-frag.glsl | 10 +- .../voxygen/shaders/light-shadows-geom.glsl | 31 +- .../voxygen/shaders/light-shadows-vert.glsl | 5 +- assets/voxygen/shaders/lod-terrain-frag.glsl | 284 +- assets/voxygen/shaders/lod-terrain-vert.glsl | 13 +- .../voxygen/shaders/player-shadow-frag.glsl | 23 +- assets/voxygen/shaders/postprocess-frag.glsl | 10 +- assets/voxygen/shaders/postprocess-vert.glsl | 2 +- assets/voxygen/shaders/skybox-frag.glsl | 2 + assets/voxygen/shaders/skybox-vert.glsl | 18 +- assets/voxygen/shaders/sprite-frag.glsl | 109 +- assets/voxygen/shaders/sprite-vert.glsl | 231 +- assets/voxygen/shaders/terrain-frag.glsl | 153 +- assets/voxygen/shaders/terrain-vert.glsl | 89 +- assets/voxygen/shaders/ui-vert.glsl | 16 +- client/Cargo.toml | 2 +- common/Cargo.toml | 2 +- common/src/comp/body.rs | 102 +- common/src/vol.rs | 2 +- common/src/volumes/scaled.rs | 30 +- server/Cargo.toml | 2 +- voxygen/Cargo.toml | 7 +- voxygen/benches/meshing_benchmark.rs | 2 +- voxygen/examples/character_renderer.rs | 15 +- voxygen/src/anim/biped_large/mod.rs | 35 +- voxygen/src/anim/bird_medium/mod.rs | 21 +- voxygen/src/anim/bird_small/mod.rs | 15 +- voxygen/src/anim/character/mod.rs | 43 +- voxygen/src/anim/critter/mod.rs | 17 +- voxygen/src/anim/dragon/mod.rs | 41 +- voxygen/src/anim/fish_medium/mod.rs | 19 +- voxygen/src/anim/fish_small/mod.rs | 11 +- voxygen/src/anim/fixture/mod.rs | 39 +- voxygen/src/anim/golem/mod.rs | 31 +- voxygen/src/anim/mod.rs | 5 +- voxygen/src/anim/object/mod.rs | 37 +- voxygen/src/anim/quadruped_medium/mod.rs | 29 +- voxygen/src/anim/quadruped_small/mod.rs | 19 +- voxygen/src/hud/mod.rs | 20 +- voxygen/src/hud/settings_window.rs | 83 +- voxygen/src/menu/char_selection/mod.rs | 13 +- voxygen/src/mesh/greedy.rs | 778 ++++ voxygen/src/mesh/mod.rs | 8 +- voxygen/src/mesh/segment.rs | 365 +- voxygen/src/mesh/terrain.rs | 413 +- voxygen/src/mesh/vol.rs | 233 -- voxygen/src/render/consts.rs | 12 +- voxygen/src/render/mesh.rs | 1 + voxygen/src/render/mod.rs | 50 +- voxygen/src/render/pipelines/figure.rs | 82 +- voxygen/src/render/pipelines/fluid.rs | 44 +- voxygen/src/render/pipelines/lod_terrain.rs | 4 +- voxygen/src/render/pipelines/mod.rs | 40 +- voxygen/src/render/pipelines/shadow.rs | 126 +- voxygen/src/render/pipelines/skybox.rs | 5 +- voxygen/src/render/pipelines/sprite.rs | 126 +- voxygen/src/render/pipelines/terrain.rs | 123 +- voxygen/src/render/pipelines/ui.rs | 12 + voxygen/src/render/renderer.rs | 1160 ++++-- voxygen/src/render/texture.rs | 52 + voxygen/src/scene/camera.rs | 21 +- voxygen/src/scene/figure/cache.rs | 333 +- voxygen/src/scene/figure/load.rs | 552 ++- voxygen/src/scene/figure/mod.rs | 1691 ++++---- voxygen/src/scene/mod.rs | 1719 +++++++- voxygen/src/scene/simple.rs | 107 +- voxygen/src/scene/terrain.rs | 3563 +++++++++-------- voxygen/src/session.rs | 55 +- voxygen/src/settings.rs | 28 +- voxygen/src/ui/cache.rs | 4 +- voxygen/src/ui/graphic/mod.rs | 4 +- voxygen/src/ui/mod.rs | 7 + voxygen/src/window.rs | 5 +- world/Cargo.toml | 3 +- world/src/civ/mod.rs | 82 + world/src/lib.rs | 2 +- world/src/util/mod.rs | 1 + 93 files changed, 10072 insertions(+), 4954 deletions(-) create mode 100644 assets/voxygen/shaders/light-shadows-directed-frag.glsl create mode 100644 assets/voxygen/shaders/light-shadows-directed-vert.glsl create mode 100644 assets/voxygen/shaders/light-shadows-figure-vert.glsl create mode 100644 voxygen/src/mesh/greedy.rs delete mode 100644 voxygen/src/mesh/vol.rs diff --git a/Cargo.lock b/Cargo.lock index c96bd1bca5..aaf5cc7c8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,25 +1274,13 @@ dependencies = [ [[package]] name = "euclid" -version = "0.19.9" +version = "0.20.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "596b99621b9477e7a5f94d2d8dd13a9c5c302ac358b822c67a42b6f1054450e1" +checksum = "555d51b9a929edb14183fad621e2d5736fc8760707a24246047288d4c142b6bd" dependencies = [ - "euclid_macros", "num-traits 0.2.11", ] -[[package]] -name = "euclid_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "failure" version = "0.1.7" @@ -1952,8 +1940,9 @@ dependencies = [ [[package]] name = "guillotiere" -version = "0.4.2" -source = "git+https://github.com/Imberflur/guillotiere#42c298f5bcf0f95f1a004360d05e25ca3711e9ed" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47065d052e2f000066c4ffbea7051e55bff5c1532c400fc1e269492b2474ccc1" dependencies = [ "euclid", "svg_fmt", @@ -4417,9 +4406,9 @@ checksum = "da5b4a0c9f3c7c8e891e445a7c776627e208e8bba23ab680798066dd283e6a15" [[package]] name = "svg_fmt" -version = "0.2.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e5f95e89d737f30cd1f98a9af9a85c2a1cc162cfedfba5a0c54cf92d7206fc" +checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" [[package]] name = "syn" @@ -4945,9 +4934,9 @@ dependencies = [ [[package]] name = "vek" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c98f7e1c1400d5b1704baee82cbc56a3fde406769555ead0f2306e43ebab967" +checksum = "2657d8704e5e0be82b60157c8dbc71a269273ad766984508fdc54030a0690c4d" dependencies = [ "approx 0.3.2", "num-integer", @@ -4981,7 +4970,7 @@ dependencies = [ "rayon", "specs", "uvth", - "vek 0.10.0", + "vek 0.11.2", "veloren-common", "veloren-world", ] @@ -5016,7 +5005,7 @@ dependencies = [ "specs", "specs-idvs", "sum_type", - "vek 0.10.0", + "vek 0.11.2", ] [[package]] @@ -5045,7 +5034,7 @@ dependencies = [ "specs", "specs-idvs", "uvth", - "vek 0.10.0", + "vek 0.11.2", "veloren-common", "veloren-world", ] @@ -5083,6 +5072,7 @@ dependencies = [ "fern", "gfx", "gfx_device_gl", + "gfx_gl", "gfx_window_glutin", "gilrs", "git2", @@ -5103,7 +5093,7 @@ dependencies = [ "specs-idvs", "treeculler", "uvth", - "vek 0.10.0", + "vek 0.11.2", "veloren-client", "veloren-common", "veloren-server", @@ -5119,6 +5109,7 @@ dependencies = [ "arr_macro", "bincode", "bitvec", + "criterion", "fxhash", "hashbrown", "image", @@ -5138,7 +5129,7 @@ dependencies = [ "roots", "serde", "serde_derive", - "vek 0.10.0", + "vek 0.11.2", "veloren-common", ] diff --git a/assets/common/npc_names.json b/assets/common/npc_names.json index 32aa922761..8d3639c7be 100644 --- a/assets/common/npc_names.json +++ b/assets/common/npc_names.json @@ -561,5 +561,33 @@ "generic": "Reddragon" } } + }, + "object": { + "body": { + "keyword": "object", + "names": [] + }, + "species": null + }, + "fish_small": { + "body": { + "keyword": "fish_small", + "names": [] + }, + "species": null + }, + "fish_medium": { + "body": { + "keyword": "fish_medium", + "names": [] + }, + "species": null + }, + "bird_small": { + "body": { + "keyword": "bird_small", + "names": [] + }, + "species": null } } diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index 2b4d3b1b77..1a9e3d211b 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -17,16 +17,40 @@ #include in vec3 f_pos; -in vec3 f_col; -in float f_ao; +// in float dummy; +// in vec3 f_col; +// in float f_ao; +// flat in uint f_pos_norm; flat in vec3 f_norm; +/*centroid */in vec2 f_uv_pos; // in float f_alt; // in vec4 f_shadow; +// in vec3 light_pos[2]; + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// in vec4 sun_pos; +// #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// const vec4 sun_pos = vec4(0.0); +// #endif + +uniform sampler2D t_col_light; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; layout (std140) uniform u_locals { mat4 model_mat; vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; // bit 0 - is player // bit 1-31 - unused int flags; @@ -34,6 +58,7 @@ uniform u_locals { struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -48,28 +73,81 @@ uniform u_bones { out vec4 tgt_color; void main() { + // vec2 texSize = textureSize(t_col_light, 0); + // vec4 col_light = texture(t_col_light, (f_uv_pos + 0.5) / texSize); + // vec3 f_col = col_light.rgb; + // float f_ao = col_light.a; + + // vec4 f_col_light = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)); + // vec3 f_col = f_col_light.rgb; + // float f_ao = f_col_light.a; + + // vec2 f_uv_pos = f_uv_pos + atlas_offs.xy; + vec4 f_col_light = texelFetch(t_col_light, ivec2(f_uv_pos)/* + uv_delta*//* - f_norm * 0.00001*/, 0); + // vec4 f_col_light = texelFetch(t_col_light, ivec2(int(f_uv_pos.x), int(f_uv_pos.y)/* + uv_delta*//* - f_norm * 0.00001*/), 0); + vec3 f_col = /*linear_to_srgb*//*srgb_to_linear*/(f_col_light.rgb); + // vec3 f_col = vec3(1.0); + // vec2 texSize = textureSize(t_col_light, 0); + float f_ao = texture(t_col_light, (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_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; + // 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); + // float f_ao = 0; + // tgt_color = vec4(vec3(f_ao), 1.0); + // tgt_color = vec4(f_col, 1.0); + // return; + + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); + + // 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); + /* 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); /* float sun_shade_frac = horizon_at(f_pos, sun_dir); float moon_shade_frac = horizon_at(f_pos, 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); +#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, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); - float moon_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, moon_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;// horizon_at2(f_shadow, f_alt, 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 = /*1.0;*/sun_shade_frac + moon_shade_frac; + // float shade_frac = /*1.0;*/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, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); vec3 surf_color = /*srgb_to_linear*/(model_col.rgb * f_col); float alpha = 1.0; @@ -86,20 +164,22 @@ void main() { vec3 emitted_light, reflected_light; - float point_shadow = shadow_at(f_pos, f_norm); // vec3 light_frac = /*vec3(1.0);*//*vec3(max(dot(f_norm, -sun_dir) * 0.5 + 0.5, 0.0));*/light_reflection_factor(f_norm, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); // vec3 point_light = light_at(f_pos, f_norm); // vec3 light, diffuse_light, ambient_light; //get_sun_diffuse(f_norm, time_of_day.x, view_dir, k_a * point_shadow * (shade_frac * 0.5 + light_frac * 0.5), k_d * point_shadow * shade_frac, k_s * point_shadow * shade_frac, alpha, emitted_light, reflected_light); float max_light = 0.0; - max_light += get_sun_diffuse2(f_norm, sun_dir, moon_dir, view_dir, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light); - reflected_light *= point_shadow * shade_frac; - emitted_light *= point_shadow * max(shade_frac, MIN_SHADOW); - max_light *= point_shadow * shade_frac; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light); + // reflected_light *= point_shadow * shade_frac; + // emitted_light *= point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= point_shadow * shade_frac; + // reflected_light *= point_shadow; + // emitted_light *= point_shadow; + // max_light *= point_shadow; max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); - float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + float ao = f_ao;//0.25 + f_ao * 0.75; ///*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; reflected_light *= ao; emitted_light *= ao; @@ -118,21 +198,25 @@ void main() { // vec3 surf_color = illuminate(srgb_to_linear(model_col.rgb * f_col), light, diffuse_light, ambient_light); surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light); +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif - if ((flags & 1) == 1 && int(cam_mode) == 1) { - float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; + // if ((flags & 1) == 1 && int(cam_mode) == 1) { + // float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; - float opacity = clamp(distance / distance_divider, 0, 1); + // float opacity = clamp(distance / distance_divider, 0, 1); - if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { - discard; - return; - } - } + // // if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { + // // discard; + // // return; + // // } + // } tgt_color = vec4(color, 1.0); } diff --git a/assets/voxygen/shaders/figure-vert.glsl b/assets/voxygen/shaders/figure-vert.glsl index 426e014290..43a9f5decc 100644 --- a/assets/voxygen/shaders/figure-vert.glsl +++ b/assets/voxygen/shaders/figure-vert.glsl @@ -16,14 +16,19 @@ #include in uint v_pos_norm; -in vec3 v_norm; -in uint v_col; -in uint v_ao_bone; +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) uniform u_locals { mat4 model_mat; vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; // bit 0 - is player // bit 1-31 - unused int flags; @@ -31,6 +36,7 @@ uniform u_locals { struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -39,46 +45,96 @@ uniform u_bones { BoneData bones[16]; }; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + out vec3 f_pos; -out vec3 f_col; -out float f_ao; +// flat out uint f_pos_norm; flat out vec3 f_norm; +// float dummy; +/*centroid */out vec2 f_uv_pos; +// out vec3 f_col; +// out float f_ao; // out float f_alt; // out vec4 f_shadow; +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// out vec4 sun_pos; +// #endif + void main() { // Pre-calculate bone matrix - uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; - mat4 combined_mat = model_mat * bones[bone_idx].bone_mat; + /* uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; */ + uint bone_idx = (v_pos_norm >> 27) & 0xFu; + BoneData bone_data = bones[bone_idx]; + mat4 bone_mat = bone_data.bone_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 = ( - combined_mat * - vec4(pos, 1)).xyz; + combined_mat * + vec4(pos, 1.0) + ).xyz + (model_pos - focus_off.xyz); - f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + /* f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); */ - f_col = srgb_to_linear(vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0); + f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(2, 17)) & uvec2(0x7FFFu, 0x7FFFu)); - f_ao = float(v_ao_bone & 0x3u) / 4.0; + // f_col = srgb_to_linear(vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0); + // f_col = vec3(1.0); + + // f_ao = float(v_ao_bone & 0x3u) / 4.0; + // f_ao = 1.0; + /* for (uint i = 0u; i < light_shadow_count.z; ++i) { + light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + } */ // 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)); - vec3 norm = normals[(v_pos_norm >> 29) & 0x7u]; + // uint normal_idx = ((v_atlas_pos & 3u) << 1u) | (v_pos_norm >> 31u); + // const 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)); + // vec3 norm = normals[normal_idx]; + uint axis_idx = v_atlas_pos & 3u; - // Calculate normal here rather than for each pixel in the fragment shader - f_norm = normalize(( - combined_mat * - vec4(norm, 0.0) - ).xyz); + vec3 norm = bone_data.normals_mat[axis_idx].xyz; + // norm = normalize(norm); + // 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); + f_norm = mix(-norm, norm, v_pos_norm >> 31u); + +// #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)/*)*/; +// // } +// 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 + + // f_pos_norm = v_pos_norm; // Also precalculate shadow texture and estimated terrain altitude. // f_alt = alt_at(f_pos.xy); // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); - gl_Position = all_mat * vec4(f_pos, 1); - // gl_Position.z = -gl_Position.z / gl_Position.w; + gl_Position = all_mat/*shadowMats[0].shadowMatrices*/ * vec4(f_pos, 1); + // gl_Position.z = -gl_Position.z / 100.0 / gl_Position.w; // gl_Position.z = -gl_Position.z / 100.0; - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // gl_Position.z = gl_Position.z / 100.0; + // gl_Position.z = -gl_Position.z; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl index e57fbdb6e6..1b876d678d 100644 --- a/assets/voxygen/shaders/fluid-frag/cheap.glsl +++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl @@ -23,14 +23,25 @@ in vec3 f_pos; flat in uint f_pos_norm; -in vec3 f_col; -in float f_light; +// in vec3 f_col; +// in float f_light; +// in vec3 light_pos[2]; +// struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +// }; +// +// layout (std140) +// uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +// }; layout (std140) uniform u_locals { vec3 model_offs; - float load_time; + float load_time; + ivec4 atlas_offs; }; uniform sampler2D t_waves; @@ -42,33 +53,59 @@ out vec4 tgt_color; #include void main() { - // 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)); + // tgt_color = vec4(1.0 - MU_WATER, 1.0); + // return; + // 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)); - // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. - uint norm_axis = (f_pos_norm >> 30) & 0x3u; - // Increase array access by 3 to access positive values - uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; - // Use an array to avoid conditional branching - vec3 f_norm = normals[norm_axis + norm_dir]; + // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. + uint norm_axis = (f_pos_norm >> 30) & 0x3u; + // Increase array access by 3 to access positive values + uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; + // Use an array to avoid conditional branching + vec3 f_norm = normals[norm_axis + norm_dir]; + + // 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 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); - /*const */vec3 water_color = 1.0 - MU_WATER;//srgb_to_linear(vec3(0.2, 0.5, 1.0)); + // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); + /*const */vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;//srgb_to_linear(vec3(0.2, 0.5, 1.0)); // /*const */vec3 water_color = srgb_to_linear(vec3(0.0, 0.25, 0.5)); - vec3 sun_dir = get_sun_dir(time_of_day.x); - vec3 moon_dir = get_moon_dir(time_of_day.x); + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) float f_alt = alt_at(f_pos.xy); +#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, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); - float moon_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, moon_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir); // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir); - float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac; + // float shade_frac = /*1.0;*/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, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); float fluid_alt = f_pos.z;//max(ceil(f_pos.z), floor(f_alt));// f_alt;//max(f_alt - f_pos.z, 0.0); @@ -96,41 +133,44 @@ void main() { vec3 emitted_light, reflected_light; - // float point_shadow = shadow_at(f_pos, f_norm); - // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); - // vec3 emitted_light, reflected_light; - // vec3 light, diffuse_light, ambient_light; - float point_shadow = shadow_at(f_pos,f_norm); + // float point_shadow = shadow_at(f_pos, f_norm); + // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec3 emitted_light, reflected_light; + // vec3 light, diffuse_light, ambient_light; // Squared to account for prior saturation. - float f_light = pow(f_light, 1.5); + // float f_light = 1.0;// pow(f_light, 1.5); // float vert_light = f_light; // vec3 light_frac = /*vec3(1.0);*/light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); - // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); + // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0)); float max_light = 0.0; - max_light += get_sun_diffuse2(f_norm, /*time_of_day.x*/sun_dir, moon_dir, /*-cam_to_frag*/sun_view_dir/*view_dir*/, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, /*vec3(0.0)*/k_d, k_s, alpha, 1.0, emitted_light, reflected_light); - reflected_light *= f_light * point_shadow * shade_frac; - emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW); - max_light *= f_light * point_shadow * shade_frac; - // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); - // diffuse_light *= f_light * point_shadow; - // ambient_light *= f_light, point_shadow; - // vec3 point_light = light_at(f_pos, f_norm); - // light += point_light; - // diffuse_light += point_light; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x*//*-cam_to_frag*/sun_view_dir/*view_dir*/, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, /*vec3(0.0)*/k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + // reflected_light *= f_light * point_shadow * shade_frac; + // emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= f_light * point_shadow * shade_frac; + // reflected_light *= f_light * point_shadow; + // emitted_light *= f_light * point_shadow; + // max_light *= f_light * point_shadow; + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light, point_shadow; + // vec3 point_light = light_at(f_pos, f_norm); + // light += point_light; + // diffuse_light += point_light; // reflected_light += point_light; - // vec3 surf_color = srgb_to_linear(vec3(0.4, 0.7, 2.0)) * light * diffuse_light * ambient_light; + // vec3 surf_color = srgb_to_linear(vec3(0.4, 0.7, 2.0)) * light * diffuse_light * ambient_light; // lights_at(f_pos, f_norm, cam_to_frag, k_a * f_light * point_shadow, k_d * f_light * point_shadow, k_s * f_light * point_shadow, alpha, emitted_light, reflected_light); /*vec3 point_light = light_at(f_pos, f_norm); emitted_light += point_light; reflected_light += point_light; */ - max_light += lights_at(f_pos, /*f_norm*/cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, 1.0, emitted_light, reflected_light); + max_light += lights_at(f_pos, /*f_norm*/cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); // vec3 diffuse_light_point = vec3(0.0); // max_light += lights_at(f_pos, f_norm, view_dir, k_a, vec3(1.0), k_s, alpha, emitted_light, diffuse_light_point); - float reflected_light_point = length(reflected_light);///*length*/(diffuse_light_point.r) + f_light * point_shadow; + // float reflected_light_point = length(reflected_light);///*length*/(diffuse_light_point.r) + f_light * point_shadow; + // float reflected_light_point = dot(reflected_light, reflected_light) * 0.5;///*length*/(diffuse_light_point.r) + f_light * point_shadow; // vec3 dump_light = vec3(0.0); // vec3 specular_light_point = vec3(0.0); // lights_at(f_pos, f_norm, view_dir, vec3(0.0), vec3(0.0), /*vec3(1.0)*/k_s, alpha, dump_light, specular_light_point); @@ -139,14 +179,20 @@ void main() { // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow; // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point; - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(cam_to_frag, time_of_day.x, cam_pos.xyz, f_pos, 0.25, true, clouds); + float passthrough = /*pow(*/dot(cam_norm, -cam_to_frag/*view_dir*/)/*, 0.5)*/; + float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); - float passthrough = /*pow(*/dot(cam_norm, -cam_to_frag/*view_dir*/)/*, 0.5)*/; + vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light); + // vec4 color = vec4(surf_color, passthrough * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam))); + vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*//*(f_light * point_shadow + point_light)*//*4.0 * reflected_light_point*/min_refl/* * 0.25*/)), passthrough); - vec3 surf_color = illuminate(max_light, view_dir, water_color * fog_color * emitted_light, /*surf_color * */water_color * reflected_light); - vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*//*(f_light * point_shadow + point_light)*/4.0 * reflected_light_point/* * 0.25*/)), passthrough); - - tgt_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + vec4 clouds; + vec3 fog_color = get_sky_color(cam_to_frag, time_of_day.x, cam_pos.xyz, f_pos, 0.25, false, clouds); + vec4 final_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec4 final_color = color; +#endif + tgt_color = final_color; } diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index 2cb0565bb4..896def6be8 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -25,13 +25,25 @@ in vec3 f_pos; flat in uint f_pos_norm; -in vec3 f_col; -in float f_light; +// in vec3 f_col; +// in float f_light; +// in vec3 light_pos[2]; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; layout (std140) uniform u_locals { - vec3 model_offs; + vec3 model_offs; float load_time; + ivec4 atlas_offs; }; uniform sampler2D t_waves; @@ -89,6 +101,16 @@ void main() { // Use an array to avoid conditional branching vec3 f_norm = normals[norm_axis + norm_dir]; + // 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*/); @@ -121,7 +143,11 @@ void main() { vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y; // vec3 norm = f_norm; +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) float f_alt = alt_at(f_pos.xy); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif float fluid_alt = max(ceil(f_pos.z), floor(f_alt));// f_alt;//max(f_alt - f_pos.z, 0.0); const float alpha = 0.255/*/ / 4.0*//* / 4.0 / sqrt(2.0)*/; @@ -144,7 +170,7 @@ void main() { reflect_ray_dir = normalize(vec3(reflect_ray_dir4) / reflect_ray_dir4.w); */ // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); // Squared to account for prior saturation. - float f_light = pow(f_light, 1.5); + float f_light = 1.0;// pow(f_light, 1.5); vec3 reflect_color = get_sky_color(/*reflect_ray_dir*/beam_view_dir, time_of_day.x, f_pos, vec3(-100000), 0.25, false, _clouds) * f_light; // /*const */vec3 water_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)); // /*const */vec3 water_color = srgb_to_linear(vec3(0.8, 0.9, 1.0)); @@ -153,14 +179,23 @@ void main() { // /*const */vec3 water_attenuation = MU_WATER;// vec3(0.8, 0.05, 0.01); // /*const */vec3 water_color = vec3(0.2, 0.95, 0.99); - vec3 sun_dir = get_sun_dir(time_of_day.x); - vec3 moon_dir = get_moon_dir(time_of_day.x); + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) 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 moon_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, moon_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;// horizon_at2(f_shadow, f_alt, f_pos, moon_dir); // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir); - float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac; + // float shade_frac = /*1.0;*/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, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); // Hack to determine water depth: color goes down with distance through water, so // we assume water color absorption from this point a to some other point b is the distance @@ -206,16 +241,18 @@ void main() { vec3 emitted_light, reflected_light; // vec3 light, diffuse_light, ambient_light; - float point_shadow = shadow_at(f_pos, f_norm); // vec3 light_frac = /*vec3(1.0);*/light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); // 0 = 100% reflection, 1 = translucent water float passthrough = /*pow(*/dot(faceforward(norm, norm, cam_to_frag/*view_dir*/), -cam_to_frag/*view_dir*/)/*, 0.5)*/; float max_light = 0.0; - max_light += get_sun_diffuse2(norm, /*time_of_day.x*/sun_dir, moon_dir, sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, 1.0, emitted_light, reflected_light); - reflected_light *= /*water_color_direct * */reflect_color * f_light * point_shadow * shade_frac; - emitted_light *= /*water_color_direct*//*ambient_attenuation * */f_light * point_shadow * max(shade_frac, MIN_SHADOW); - max_light *= f_light * point_shadow * shade_frac; + max_light += get_sun_diffuse2(sun_info, moon_info, norm, /*time_of_day.x*/sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); + // reflected_light *= /*water_color_direct * */reflect_color * f_light * point_shadow * shade_frac; + // emitted_light *= /*water_color_direct*//*ambient_attenuation * */f_light * point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= f_light * point_shadow * shade_frac; + // reflected_light *= /*water_color_direct * */reflect_color * f_light * point_shadow; + // emitted_light *= /*water_color_direct*//*ambient_attenuation * */f_light * point_shadow; + // max_light *= f_light * point_shadow; // vec3 diffuse_light_point = vec3(0.0); // max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, vec3(1.0), /*vec3(0.0)*/k_s, alpha, emitted_light, diffuse_light_point); @@ -226,7 +263,7 @@ void main() { // diffuse_light_point -= specular_light_point; // max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, /*k_d*/vec3(0.0), /*vec3(0.0)*/k_s, alpha, emitted_light, /*diffuse_light*/reflected_light); - max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, /*k_d*//*vec3(0.0)*/k_d, /*vec3(0.0)*/k_s, alpha, 1.0, emitted_light, /*diffuse_light*/reflected_light); + max_light += lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, /*k_d*//*vec3(0.0)*/k_d, /*vec3(0.0)*/k_s, alpha, f_norm, 1.0, emitted_light, /*diffuse_light*/reflected_light); float reflected_light_point = length(reflected_light);///*length*/(diffuse_light_point.r) + f_light * point_shadow; // TODO: See if we can be smarter about this using point light distances. @@ -246,10 +283,6 @@ void main() { // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)) * light * diffuse_light * ambient_light; vec3 surf_color = illuminate(max_light, view_dir, emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflected_light/* * log(1.0 - MU_WATER)*/); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(cam_to_frag/*-view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.25, true, clouds); - // passthrough = pow(passthrough, 1.0 / (1.0 + water_depth_to_camera)); /* surf_color = cam_attenuation.g < 0.5 ? vec3(1.0, 0.0, 0.0) : @@ -284,5 +317,13 @@ void main() { vec4 color = mix(vec4(reflect_color, 1.0), vec4(vec3(0), 1.0 / (1.0 + diffuse_light * 0.25)), passthrough); */ - tgt_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + vec4 clouds; + vec3 fog_color = get_sky_color(cam_to_frag/*-view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.25, false, clouds); + vec4 final_color = mix(mix(color, vec4(fog_color, 0.0), fog_level), vec4(clouds.rgb, 0.0), clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec4 final_color = color; +#endif + tgt_color = final_color; } diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl index 0f6c9f7c31..3323a8056a 100644 --- a/assets/voxygen/shaders/fluid-vert.glsl +++ b/assets/voxygen/shaders/fluid-vert.glsl @@ -21,48 +21,63 @@ #include in uint v_pos_norm; -in uint v_col_light; +// in uint v_col_light; layout (std140) uniform u_locals { vec3 model_offs; - float load_time; + float load_time; + ivec4 atlas_offs; }; +// struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +// }; +// +// layout (std140) +// uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +// }; + out vec3 f_pos; flat out uint f_pos_norm; -out vec3 f_col; -out float f_light; +// out vec3 f_col; +// out float f_light; +// out vec3 light_pos[2]; -const float EXTRA_NEG_Z = 65536.0; +const float EXTRA_NEG_Z = /*65536.0*/65536.1; void main() { - f_pos = vec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0x1FFFFu)) - vec3(0, 0, EXTRA_NEG_Z) + model_offs; - // f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); - // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); + f_pos = vec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0x1FFFFu)) - vec3(0, 0, EXTRA_NEG_Z) + model_offs - focus_off.xyz; + // f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); + // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); - // Small waves - f_pos.xy += 0.01; // Avoid z-fighting - // f_pos.x += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 1.0))); - // f_pos.y += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 2.0))); + // Small waves + // f_pos.xy += 0.01; // Avoid z-fighting + // f_pos.x += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 1.0))); + // f_pos.y += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 2.0))); #if (FLUID_MODE == FLUID_MODE_SHINY) - f_pos.z -= 0.1 + 0.1 * (sin(tick.x/* / 60.0*/* 2.0 + f_pos.x * 2.0 + f_pos.y * 2.0) + 1.0) * 0.5; + // f_pos.z -= 0.1 + 0.1 * (sin(tick.x/* / 60.0*/* 2.0 + f_pos.x * 2.0 + f_pos.y * 2.0) + 1.0) * 0.5; #endif - f_col = vec3( - float((v_col_light >> 8) & 0xFFu), - float((v_col_light >> 16) & 0xFFu), - float((v_col_light >> 24) & 0xFFu) + /* f_col = vec3( + float((v_col_light >> 8) & 0xFFu), + float((v_col_light >> 16) & 0xFFu), + float((v_col_light >> 24) & 0xFFu) ) / 255.0; - f_light = float(v_col_light & 0xFFu) / 255.0; + f_light = float(v_col_light & 0xFFu) / 255.0; */ + /* for (uint i = 0u; i < light_shadow_count.z; ++i) { + light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + } */ - f_pos_norm = v_pos_norm; + f_pos_norm = v_pos_norm; gl_Position = - all_mat * - vec4(f_pos, 1); + all_mat * + vec4(f_pos, 1); // gl_Position.z = -gl_Position.z / gl_Position.w; - // gl_Position.z = -gl_Position.z / 100.0; - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/include/constants.glsl b/assets/voxygen/shaders/include/constants.glsl index 4e4d9e4d61..43e19628e3 100644 --- a/assets/voxygen/shaders/include/constants.glsl +++ b/assets/voxygen/shaders/include/constants.glsl @@ -16,6 +16,10 @@ #define LIGHTING_ALGORITHM_BLINN_PHONG 1u #define LIGHTING_ALGORITHM_ASHIKHMIN 2u +#define SHADOW_MODE_NONE 0u +#define SHADOW_MODE_CHEAP 1u +#define SHADOW_MODE_MAP 2u + /* Unlike the other flags (for now anyway), these are bitmask values */ #define LIGHTING_TYPE_REFLECTION 0x01u #define LIGHTING_TYPE_TRANSMISSION 0x02u @@ -43,6 +47,7 @@ #define FLUID_MODE #define CLOUD_MODE #define LIGHTING_ALGORITHM +#define SHADOW_MODE */ /* Constants expected to be defined by any shader that needs to perform lighting calculations @@ -56,3 +61,11 @@ #define LIGHTING_DISTRIBUTION_SCHEME #define LIGHTING_DISTRIBUTION */ + +/* Constants that *may* be defined by any shader. + * (and whose values may take automatically defined constants into account): */ + +/* +// When sets, shadow maps are used to cast shadows. +#define HAS_SHADOW_MAPS +*/ diff --git a/assets/voxygen/shaders/include/globals.glsl b/assets/voxygen/shaders/include/globals.glsl index 69450dadb2..b1ceefe8bd 100644 --- a/assets/voxygen/shaders/include/globals.glsl +++ b/assets/voxygen/shaders/include/globals.glsl @@ -4,9 +4,12 @@ uniform u_globals { mat4 proj_mat; mat4 all_mat; vec4 cam_pos; + vec4 focus_off; vec4 focus_pos; vec4 view_distance; vec4 time_of_day; + vec4 sun_dir; + vec4 moon_dir; vec4 tick; vec4 screen_res; uvec4 light_shadow_count; diff --git a/assets/voxygen/shaders/include/light.glsl b/assets/voxygen/shaders/include/light.glsl index 3c2d490e4a..f2247b499b 100644 --- a/assets/voxygen/shaders/include/light.glsl +++ b/assets/voxygen/shaders/include/light.glsl @@ -1,4 +1,5 @@ #include +#include struct Light { vec4 light_pos; @@ -8,7 +9,7 @@ struct Light { layout (std140) uniform u_lights { - Light lights[32]; + Light lights[31]; }; struct Shadow { @@ -26,116 +27,6 @@ float attenuation_strength(vec3 rpos) { return max(2.0 / pow(d2 + 10, 0.35) - pow(d2 / 50000.0, 0.8), 0.0); } -#ifdef HAS_SHADOW_MAPS -// uniform samplerCubeArrayShadow t_shadow_maps; -// uniform samplerCubeArray t_shadow_maps; -uniform samplerCubeShadow t_shadow_maps; -// uniform samplerCube t_shadow_maps; - -// uniform sampler2DArray t_directed_shadow_maps; - -float VectorToDepth (vec3 Vec) -{ - vec3 AbsVec = abs(Vec); - float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z)); - // float LocalZcomp = length(Vec); - - // Replace f and n with the far and near plane values you used when - // you drew your cube map. - // const float f = 2048.0; - // const float n = 1.0; - - // float NormZComp = (screen_res.w+screen_res.z) / (screen_res.w-screen_res.z) - (2*screen_res.w*screen_res.z)/(screen_res.w-screen_res.z)/LocalZcomp; - // float NormZComp = 1.0 - shadow_proj_factors.y / shadow_proj_factors.x / LocalZcomp; - float NormZComp = shadow_proj_factors.x - shadow_proj_factors.y / LocalZcomp; - // NormZComp = -1000.0 / (NormZComp + 10000.0); - return (NormZComp + 1.0) * 0.5; - - // float NormZComp = length(LocalZcomp); - // NormZComp = -NormZComp / screen_res.w; - // // return (NormZComp + 1.0) * 0.5; - // return NormZComp; -} - -const vec3 sampleOffsetDirections[20] = vec3[] -( - vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), - vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), - vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), - vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), - vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) - // vec3(0, 0, 0) -); - -float ShadowCalculation(uint lightIndex, vec3 fragToLight, /*float currentDepth*/vec3 fragPos) -{ - if (lightIndex != 0u) { - return 1.0; - }; - - float shadow = 0.0; - float bias = 0.0;//-0.003;//-0.003;//-0.005;//0.001;//-1.0;//-0.001;//0.001;//0.003;//-0.05;//-0.1;//0.0;//0.1 - - { - float currentDepth = VectorToDepth(fragToLight);// + bias; - - float visibility = texture(t_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; - } - // return visibility == 1.0 ? 1.0 : 0.0; - } - - int samples = 20; - float lightDistance = length(fragToLight); - float viewDistance = length(cam_pos.xyz - fragPos); - // float diskRadius = 0.00001; - // float diskRadius = 1.0; - // float diskRadius = 0.05; - float diskRadius = (1.0 + (/*viewDistance*/viewDistance / screen_res.w)) / 25.0; - // float diskRadius = lightDistance; - for(int i = 0; i < samples; ++i) - { - float currentDepth = VectorToDepth(fragToLight + sampleOffsetDirections[i] * diskRadius) + bias; - // float closestDepth = texture(depthMap, fragToLight).r; - // closestDepth *= far_plane; // Undo mapping [0;1] - /* if(currentDepth - bias > closestDepth) - shadow += 1.0;*/ - float visibility = texture(t_shadow_maps, vec4(fragToLight, currentDepth)/*, -2.5*/); - shadow += visibility; - // float closestDepth = texture(t_shadow_maps, vec3(fragToLight)/*, -2.5*/).r; - // shadow += closestDepth > currentDepth ? 1.0 : 0.0; - } - shadow /= float(samples); - // shadow = shadow * shadow * (3.0 - 2.0 * shadow); - - // use the light to fragment vector to sample from the depth map - // float bias = 0.0;///*0.05*/0.01;//0.05;// 0.05; - // float closestDepth = texture(t_shadow_maps, /*vec4*/vec3(fragToLight/*, (lightIndex + 1)*//* * 6*/)/*, 0.0*//*, 0.0*//*, bias*/).r; - // // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, lightIndex), bias); - // // // it is currently in linear range between [0,1]. Re-transform back to original value - // closestDepth = (closestDepth + 0.0) * screen_res.w; // far plane - // // // now test for shadows - // // // float shadow = /*currentDepth*/(screen_res.w - bias) > closestDepth ? 1.0 : 0.0; - // float shadow = currentDepth - bias < closestDepth ? 1.0 : 0.0; - // float visibility = textureProj(t_shadow_maps, vec4(fragToLight, lightIndex), bias); - // float visibility = texture(t_shadow_maps, vec4(fragToLight, lightIndex + 1), -(currentDepth/* + screen_res.z*/) / screen_res.w);// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); - // currentDepth += bias; - // currentDepth = -1000.0 / (currentDepth + 10000.0); - // currentDepth /= screen_res.w; - // float currentDepth = VectorToDepth(fragToLight) + bias; - - // float visibility = texture(t_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); - // return visibility == 1.0 ? 1.0 : 0.0; - return shadow; -} -#else -float ShadowCalculation(uint lightIndex, vec3 fragToLight, /*float currentDepth*/vec3 fragPos) -{ - return 1.0; -} -#endif - // // Compute attenuation due to light passing through a substance that fills an area below a horizontal plane // // (e.g. in most cases, water below the water surface depth). // // @@ -177,7 +68,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { // Only access the array once Light L = lights[i]; - vec3 light_pos = L.light_pos.xyz; + vec3 light_pos = L.light_pos.xyz - focus_off.xyz; // Pre-calculate difference between light and fragment vec3 difference = light_pos - wpos; @@ -195,12 +86,15 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { float shadow_at(vec3 wpos, vec3 wnorm) { float shadow = 1.0; +#if (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_MAP) + return shadow; +#elif (SHADOW_MODE == SHADOW_MODE_CHEAP) for (uint i = 0u; i < light_shadow_count.y; i ++) { // Only access the array once Shadow S = shadows[i]; - vec3 shadow_pos = S.shadow_pos_radius.xyz; + vec3 shadow_pos = S.shadow_pos_radius.xyz - focus_off.xyz; float radius = S.shadow_pos_radius.w; vec3 diff = shadow_pos - wpos; @@ -209,11 +103,15 @@ float shadow_at(vec3 wpos, vec3 wnorm) { } float shade = max(pow(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, 0.25) / pow(radius * radius * 0.5, 0.25), 0.5); + // float shade = max(pow(dot(diff, diff) / (radius * radius * 0.5), 0.25), 0.5); + // float shade = dot(diff, diff) / (radius * radius * 0.5); shadow = min(shadow, shade); } // NOTE: Squared to compenate for prior saturation. - return min(shadow * shadow, 1.0); + return min(shadow, 1.0); + // return min(shadow * shadow, 1.0); +#endif } // Returns computed maximum intensity. @@ -221,20 +119,21 @@ float shadow_at(vec3 wpos, vec3 wnorm) { // mu is the attenuation coefficient for any substance on a horizontal plane. // cam_attenuation is the total light attenuation due to the substance for beams between the point and the camera. // surface_alt is the altitude of the attenuating surface. -float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, vec3 cam_attenuation, float surface_alt, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, float voxel_lighting, inout vec3 emitted_light, inout vec3 reflected_light/*, out float shadow*/) { +float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, vec3 cam_attenuation, float surface_alt, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting, inout vec3 emitted_light, inout vec3 reflected_light/*, out float shadow*/) { + // return 0.0; // shadow = 0.0; - vec3 ambient_light = vec3(0.0); + // vec3 ambient_light = vec3(0.0); vec3 directed_light = vec3(0.0); vec3 max_light = vec3(0.0); - const float LIGHT_AMBIENCE = 0.015625; + const float LIGHT_AMBIENCE = 0.0;//0.015625; - for (uint i = 0u; i < light_shadow_count.x/*32u*/; i ++) { + for (uint i = 0u; i < /*light_shadow_count.x*//*0u*/light_shadow_count.x/*32u*/; i ++) { // Only access the array once Light L = lights[i]; - vec3 light_pos = L.light_pos.xyz; + vec3 light_pos = L.light_pos.xyz - focus_off.xyz; // Pre-calculate difference between light and fragment vec3 difference = light_pos - wpos; @@ -282,10 +181,10 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 is_direct = true; #endif - vec3 direct_light = PI * color * strength * square_factor * light_reflection_factor(/*direct_norm_dir*/wnorm, /*cam_to_frag*/view_dir, direct_light_dir, k_d, k_s, alpha, voxel_lighting); - float computed_shadow = ShadowCalculation(i, -difference, wpos/*, light_distance*/); - directed_light += is_direct ? max(computed_shadow, /*LIGHT_AMBIENCE*/0.0) * direct_light * square_factor : vec3(0.0); - // directed_light += is_direct ? mix(LIGHT_AMBIENCE, 1.0, computed_shadow) * direct_light * square_factor : vec3(0.0); + vec3 direct_light = PI * color * strength * square_factor * light_reflection_factor(/*direct_norm_dir*/wnorm, /*cam_to_frag*/view_dir, direct_light_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting); + float computed_shadow = ShadowCalculationPoint(i, -difference, wnorm, wpos/*, light_distance*/); + // directed_light += is_direct ? max(computed_shadow, /*LIGHT_AMBIENCE*/0.0) * direct_light * square_factor : vec3(0.0); + directed_light += is_direct ? mix(LIGHT_AMBIENCE, 1.0, computed_shadow) * direct_light * square_factor : vec3(0.0); // ambient_light += is_direct ? vec3(0.0) : vec3(0.0); // direct_light * square_factor * LIGHT_AMBIENCE; // ambient_light += is_direct ? direct_light * (1.0 - square_factor * LIGHT_AMBIENCE) : vec3(0.0); @@ -308,7 +207,7 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve // mix(cam_strength, strength, cam_distance_2 / (cam_distance_2 + distance_2)); // max(cam_strength, strength);//mix(cam_strength, strength, clamp(distance_2 / /*pos_distance_2*/cam_distance_2, 0.0, 1.0)); // float both_strength = mix(cam_strength, strength, cam_distance_2 / sqrt(cam_distance_2 + distance_2)); - max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*//*max(both_strength, 1.0) * *//*cam_strength*//*computed_shadow * */both_strength * square_factor * square_factor * PI * color; + max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*//*max(both_strength, 1.0) * *//*cam_strength*/computed_shadow * both_strength * square_factor * square_factor * PI * color; // max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*/max(cam_strength, 1.0/*, strength*//*1.0*/) * square_factor * square_factor * PI * color; // light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIENCE); // Compute emiittance. @@ -321,11 +220,11 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve // shadow = shadow_at(wpos, wnorm); // float shadow = shadow_at(wpos, wnorm); reflected_light += directed_light; - emitted_light += k_a * ambient_light/* * shadow*/;// min(shadow, 1.0); + // emitted_light += k_a * ambient_light/* * shadow*/;// min(shadow, 1.0); return /*rel_luminance(ambient_light + directed_light)*/rel_luminance(max_light);//ambient_light; } // Same as lights_at, but with no assumed attenuation due to fluid. float lights_at(vec3 wpos, vec3 wnorm, vec3 view_dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, inout vec3 emitted_light, inout vec3 reflected_light) { - return lights_at(wpos, wnorm, view_dir, vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, 1.0, emitted_light, reflected_light); + return lights_at(wpos, wnorm, view_dir, vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, wnorm, 1.0, emitted_light, reflected_light); } diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl index a788e096f7..88f6921ea1 100644 --- a/assets/voxygen/shaders/include/lod.glsl +++ b/assets/voxygen/shaders/include/lod.glsl @@ -10,13 +10,13 @@ const float MIN_SHADOW = 0.33; vec2 pos_to_uv(sampler2D sampler, vec2 pos) { // Want: (pixel + 0.5) / W vec2 texSize = textureSize(sampler, 0); - vec2 uv_pos = (pos + 16) / (32.0 * texSize); + vec2 uv_pos = (focus_off.xy + pos + 16) / (32.0 * texSize); return vec2(uv_pos.x, 1.0 - uv_pos.y); } vec2 pos_to_tex(vec2 pos) { // Want: (pixel + 0.5) - vec2 uv_pos = (pos + 16) / 32.0; + vec2 uv_pos = (focus_off.xy + pos + 16) / 32.0; return vec2(uv_pos.x, uv_pos.y); } @@ -73,7 +73,7 @@ vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { } float alt_at(vec2 pos) { - return (texture/*textureBicubic*/(t_map, pos_to_uv(t_map, pos)).a * (/*1300.0*//*1278.7266845703125*/view_distance.w) + /*140.0*/view_distance.z); + return (texture/*textureBicubic*/(t_map, pos_to_uv(t_map, pos)).a * (/*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 @@ -87,7 +87,7 @@ float alt_at_real(vec2 pos) { // #if (FLUID_MODE == FLUID_MODE_CHEAP) // return alt_at(pos); // #elif (FLUID_MODE == FLUID_MODE_SHINY) - return (textureBicubic(t_map, pos_to_tex(pos)).a * (/*1300.0*//*1278.7266845703125*/view_distance.w) + /*140.0*/view_distance.z); + return (textureBicubic(t_map, pos_to_tex(pos)).a * (/*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; @@ -98,7 +98,7 @@ float alt_at_real(vec2 pos) { } -float horizon_at2(vec4 f_horizons, float alt, vec3 pos, /*float time_of_day*/vec3 light_dir) { +float horizon_at2(vec4 f_horizons, float alt, vec3 pos, /*float time_of_day*/vec4 light_dir) { // vec3 sun_dir = get_sun_dir(time_of_day); const float PI_2 = 3.1415926535897932384626433832795 / 2.0; const float MIN_LIGHT = 0.0;//0.115/*0.0*/; @@ -133,9 +133,9 @@ float horizon_at2(vec4 f_horizons, float alt, vec3 pos, /*float time_of_day*/vec .unwrap_or(1.0); */ // vec2 f_horizon; - if (light_dir.z >= 0) { + /* if (light_dir.z >= 0) { return 0.0; - } + } */ /* if (light_dir.x >= 0) { f_horizon = f_horizons.rg; // f_horizon = f_horizons.ba; @@ -159,7 +159,7 @@ float horizon_at2(vec4 f_horizons, float alt, vec3 pos, /*float time_of_day*/vec } */ float height = f_horizon.y * /*1300.0*//*1278.7266845703125*/view_distance.w + view_distance.z; const float w = 0.1; - float deltah = height - alt; + float deltah = height - alt - focus_off.z; //if (deltah < 0.0001/* || angle < 0.0001 || abs(light_dir.x) < 0.0001*/) { // return 1.0; /*} else */{ @@ -187,12 +187,12 @@ float horizon_at2(vec4 f_horizons, float alt, vec3 pos, /*float time_of_day*/vec } } -float horizon_at(vec3 pos, /*float time_of_day*/vec3 light_dir) { - vec4 f_horizons = textureBicubic(t_horizon, pos_to_tex(pos.xy)); - f_horizons.xyz = /*linear_to_srgb*/(f_horizons.xyz); - float alt = alt_at_real(pos.xy); - return horizon_at2(f_horizons, alt, pos, light_dir); -} +// float horizon_at(vec3 pos, /*float time_of_day*/vec3 light_dir) { +// vec4 f_horizons = textureBicubic(t_horizon, pos_to_tex(pos.xy)); +// // f_horizons.xyz = /*linear_to_srgb*/(f_horizons.xyz); +// float alt = alt_at_real(pos.xy); +// return horizon_at2(f_horizons, alt, pos, light_dir); +// } vec2 splay(vec2 pos) { // const float SPLAY_MULT = 1048576.0; diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index 5559eedf0b..8251746fe3 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -2,6 +2,15 @@ #include #include #include +#include + +// Information about an approximately directional light, like the sun or moon. +struct DirectionalLight { + // vec3 dir; + float shadow; + // vec3 color; + // float brightness; +}; const float PI = 3.141592; @@ -32,52 +41,81 @@ const float SUN_COLOR_FACTOR = 6.0;//6.0;// * 1.5;//1.8; const float UNDERWATER_MIST_DIST = 100.0; -vec3 get_sun_dir(float time_of_day) { - const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); - - float sun_angle_rad = time_of_day * TIME_FACTOR; - // return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); - return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); -} - -vec3 get_moon_dir(float time_of_day) { - const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); - - float moon_angle_rad = time_of_day * TIME_FACTOR; - // -cos((60+60*4)/360*2*pi)-0.5 = 0 - // -cos((60+60*5)/360*2*pi)-0.5 = -0.5 - // -cos((60+60*6)/360*2*pi)-0.5 = 0 - // - // i.e. moon out from (60*5)/360*24 = 20:00 to (60*7/360*24) = 28:00 = 04:00. - // - // Then sun out from 04:00 to 20:00. - return normalize(-vec3(sin(moon_angle_rad), 0.0, cos(moon_angle_rad) - 0.5)); -} - const float PERSISTENT_AMBIANCE = 1.0 / 512;// 1.0 / 512; // 0.00125 // 0.1;// 0.025; // 0.1; -float get_sun_brightness(vec3 sun_dir) { - return max(-sun_dir.z + 0.6, 0.0) * 0.9; +//vec3 get_sun_dir(float time_of_day) { +// const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); +// +// float sun_angle_rad = time_of_day * TIME_FACTOR; +// // return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); +// return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); +//} +// +//vec3 get_moon_dir(float time_of_day) { +// const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0); +// +// float moon_angle_rad = time_of_day * TIME_FACTOR; +// // -cos((60+60*4)/360*2*pi)-0.5 = 0 +// // -cos((60+60*5)/360*2*pi)-0.5 = -0.5 +// // -cos((60+60*6)/360*2*pi)-0.5 = 0 +// // +// // i.e. moon out from (60*5)/360*24 = 20:00 to (60*7/360*24) = 28:00 = 04:00. +// // +// // Then sun out from 04:00 to 20:00. +// return normalize(-vec3(sin(moon_angle_rad), 0.0, cos(moon_angle_rad) - 0.5)); +//} + +float get_sun_brightness(/*vec3 sun_dir*/) { + return max(-sun_dir.z + 0.6, 0.0) * 0.9; } -float get_moon_brightness(vec3 moon_dir) { - return max(-moon_dir.z + 0.6, 0.0) * 0.007; +float get_moon_brightness(/*vec3 moon_dir*/) { + return max(-moon_dir.z + 0.6, 0.0) * 0.007; } -vec3 get_sun_color(vec3 sun_dir) { - return mix( - mix( - DUSK_LIGHT, - NIGHT_LIGHT, - max(sun_dir.z, 0) - ), - DAY_LIGHT, - max(-sun_dir.z, 0) - ); +vec3 get_sun_color(/*vec3 sun_dir*/) { + return mix( + mix( + DUSK_LIGHT, + NIGHT_LIGHT, + max(sun_dir.z, 0) + ), + DAY_LIGHT, + max(-sun_dir.z, 0) + ); } -vec3 get_moon_color(vec3 moon_dir) { - return vec3(0.05, 0.05, 0.6); +vec3 get_moon_color(/*vec3 moon_dir*/) { + return vec3(0.05, 0.05, 0.6); +} + +DirectionalLight get_sun_info(vec4 _dir, float shade_frac/*, vec4 light_pos[2]*/, /*vec4 sun_pos*/vec3 f_pos) { + float shadow = shade_frac; +#ifdef HAS_SHADOW_MAPS +#if (SHADOW_MODE == SHADOW_MODE_MAP) + if (sun_dir.z < /*0.6*/0.0) { + /* ShadowLocals sun_shadow = shadowMats[0]; + vec4 sun_pos = sun_shadow.texture_mat * vec4(f_pos, 1.0); */ +// #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)/*)*/; +// // } +// #elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +// #endif + shadow = min(shadow, ShadowCalculationDirected(/*sun_pos, *//*0u*/f_pos)); + } +#endif +#endif + return DirectionalLight(/*dir, */shade_frac * shadow/*, get_sun_color(dir), get_sun_brightness(dir)*/); +} + +DirectionalLight get_moon_info(vec4 _dir, float shade_frac/*, vec4 light_pos[2]*/) { + float shadow = shade_frac; +// #ifdef HAS_SHADOW_MAPS +// shadow = min(shade_frac, ShadowCalculationDirected(light_pos, 1u)); +// #endif + return DirectionalLight(/*dir, */shadow/*, get_moon_color(dir), get_moon_brightness(dir)*/); } // // Calculates extra emission and reflectance (due to sunlight / moonlight). @@ -92,19 +130,19 @@ vec3 get_moon_color(vec3 moon_dir) { // // anything interesting here. // // void get_sun_diffuse(vec3 norm, float time_of_day, out vec3 light, out vec3 diffuse_light, out vec3 ambient_light, float diffusion // void get_sun_diffuse(vec3 norm, float time_of_day, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, out vec3 emitted_light, out vec3 reflected_light) { -// const float SUN_AMBIANCE = 0.1 / 2.0;// 0.1 / 3.0; +// const float SUN_AMBIANCE = 0.1 / 2.0;// 0.1 / 3.0; // -// vec3 sun_dir = get_sun_dir(time_of_day); -// vec3 moon_dir = get_moon_dir(time_of_day); +// vec3 sun_dir = get_sun_dir(time_of_day); +// vec3 moon_dir = get_moon_dir(time_of_day); // -// float sun_light = get_sun_brightness(sun_dir); -// float moon_light = get_moon_brightness(moon_dir); +// float sun_light = get_sun_brightness(sun_dir); +// float moon_light = get_moon_brightness(moon_dir); // -// vec3 sun_color = get_sun_color(sun_dir); -// vec3 moon_color = get_moon_color(moon_dir); +// vec3 sun_color = get_sun_color(sun_dir); +// vec3 moon_color = get_moon_color(moon_dir); // -// vec3 sun_chroma = sun_color * sun_light; -// vec3 moon_chroma = moon_color * moon_light; +// vec3 sun_chroma = sun_color * sun_light; +// vec3 moon_chroma = moon_color * moon_light; // // /* float NLsun = max(dot(-norm, sun_dir), 0); // float NLmoon = max(dot(-norm, moon_dir), 0); @@ -121,12 +159,12 @@ vec3 get_moon_color(vec3 moon_dir) { // sun_chroma * light_reflection_factor(norm, dir, sun_dir, k_d, k_s, alpha) + // moon_chroma * 1.0 * /*4.0 * */light_reflection_factor(norm, dir, moon_dir, k_d, k_s, alpha); // -// /* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; -// diffuse_light = -// sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + -// moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + -// PERSISTENT_AMBIANCE; -// ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); */ +// /* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; +// diffuse_light = +// sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + +// moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + +// PERSISTENT_AMBIANCE; +// ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); */ // } // Returns computed maximum intensity. @@ -135,22 +173,38 @@ vec3 get_moon_color(vec3 moon_dir) { // mu is the attenuation coefficient for any substance on a horizontal plane. // cam_attenuation is the total light attenuation due to the substance for beams between the point and the camera. // surface_alt is the altitude of the attenuating surface. -float get_sun_diffuse2(vec3 norm, vec3 sun_dir, vec3 moon_dir, vec3 dir, vec3 wpos, vec3 mu, vec3 cam_attenuation, float surface_alt, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, float voxel_lighting, out vec3 emitted_light, out vec3 reflected_light) { - const vec3 SUN_AMBIANCE = MU_SCATTER;//0.23;/* / 1.8*/;// 0.1 / 3.0; - const vec3 MOON_AMBIANCE = MU_SCATTER;//0.23;//0.1; +float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, vec3 norm, vec3 dir, vec3 wpos, vec3 mu, vec3 cam_attenuation, float surface_alt, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting, out vec3 emitted_light, out vec3 reflected_light) { + const float MIN_SHADOW = 0.15; + const vec3 SUN_AMBIANCE = MU_SCATTER;//0.23;/* / 1.8*/;// 0.1 / 3.0; + const vec3 MOON_AMBIANCE = MU_SCATTER;//0.23;//0.1; - float sun_light = get_sun_brightness(sun_dir); - float moon_light = get_moon_brightness(moon_dir); + /* vec3 sun_dir = sun_info.dir; + vec3 moon_dir = moon_info.dir; */ + vec3 sun_dir = sun_dir.xyz; + vec3 moon_dir = moon_dir.xyz; - vec3 sun_color = get_sun_color(sun_dir) * SUN_COLOR_FACTOR; - vec3 moon_color = get_moon_color(moon_dir); + float sun_light = get_sun_brightness(/*sun_dir*/);//sun_info.brightness;; + float moon_light = get_moon_brightness(/*moon_dir*/);//moon_info.brightness; + + vec3 sun_color = get_sun_color(/*sun_dir*/) * SUN_COLOR_FACTOR;//sun_info.color * SUN_COLOR_FACTOR; + vec3 moon_color = get_moon_color(/*moon_dir*/);//moon_info.color; // If the sun is facing the wrong way, we currently just want zero light, hence default point is wpos. vec3 sun_attenuation = compute_attenuation(wpos, -sun_dir, mu, surface_alt, wpos); vec3 moon_attenuation = compute_attenuation(wpos, -moon_dir, mu, surface_alt, wpos); - vec3 sun_chroma = sun_color * sun_light * cam_attenuation * sun_attenuation; - vec3 moon_chroma = moon_color * moon_light * cam_attenuation * moon_attenuation; + vec3 sun_chroma = sun_color * sun_light * cam_attenuation * sun_attenuation; + vec3 moon_chroma = moon_color * moon_light * cam_attenuation * moon_attenuation; + +// #ifdef HAS_SHADOW_MAPS +// float sun_shadow = ShadowCalculationDirected(light_pos, 0u); +// float moon_shadow = ShadowCalculationDirected(light_pos, 1u); +// #else +// float sun_shadow = 1.0; +// float moon_shadow = 1.0; +// #endif + float sun_shadow = sun_info.shadow; + float moon_shadow = moon_info.shadow; // https://en.m.wikipedia.org/wiki/Diffuse_sky_radiation // @@ -239,7 +293,7 @@ float get_sun_diffuse2(vec3 norm, vec3 sun_dir, vec3 moon_dir, vec3 dir, vec3 wp vec3 R_t_r = R_d + R_r; // vec3 half_vec = normalize(-norm + dir); - vec3 light_frac = R_t_b * (sun_chroma * SUN_AMBIANCE + moon_chroma * MOON_AMBIANCE) * light_reflection_factor(norm, /*norm*//*dir*/dir, /*-norm*/-dir, /*k_d*/k_d/* * (1.0 - k_s)*/, /*k_s*/vec3(0.0), alpha, voxel_lighting); + vec3 light_frac = R_t_b * (sun_chroma * SUN_AMBIANCE + moon_chroma * MOON_AMBIANCE) * light_reflection_factor(norm, /*norm*//*dir*/dir, /*-norm*/-/*dir*/norm, /*k_d*/k_d/* * (1.0 - k_s)*/, /*k_s*/vec3(0.0), alpha, voxel_norm, voxel_lighting); // vec3 light_frac = /*vec3(1.0)*//*H_d * */ // SUN_AMBIANCE * /*sun_light*/sun_chroma * light_reflection_factor(norm, dir, /*vec3(0, 0, -1.0)*/-norm, vec3((1.0 + cos_sun) * 0.5), vec3(k_s * (1.0 - cos_sun) * 0.5), alpha) + // MOON_AMBIANCE * /*sun_light*/moon_chroma * light_reflection_factor(norm, dir, /*vec3(0, 0, -1.0)*/-norm, vec3((1.0 + cos_moon) * 0.5), vec3(k_s * (1.0 - cos_moon) * 0.5), alpha); @@ -254,145 +308,160 @@ float get_sun_diffuse2(vec3 norm, vec3 sun_dir, vec3 moon_dir, vec3 dir, vec3 wp // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-norm, sun_dir)) * mix(0.0, 1.0, abs(sun_dir.z) * 10000.0) * 10000.0), 0.0, 0.5); // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-norm, sun_dir)) * mix(0.0, 1.0, abs(sun_dir.z) * 10000.0) * 10000.0), 0.0, 0.5); - emitted_light = k_a * light_frac + PERSISTENT_AMBIANCE; + emitted_light = light_frac + k_a * PERSISTENT_AMBIANCE; // emitted_light = k_a * light_frac * (/*ambient_sides + */SUN_AMBIANCE * /*sun_light*/sun_chroma + /*vec3(moon_light)*/MOON_AMBIANCE * moon_chroma) + PERSISTENT_AMBIANCE; - // TODO: Add shadows. reflected_light = R_t_r * ( - (1.0 - SUN_AMBIANCE) * sun_chroma * (light_reflection_factor(norm, dir, sun_dir, k_d, k_s, alpha, voxel_lighting) /*+ + (1.0 - SUN_AMBIANCE) * sun_chroma * sun_shadow * (light_reflection_factor(norm, dir, sun_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting) /*+ light_reflection_factor(norm, dir, normalize(sun_dir + vec3(0.0, 0.1, 0.0)), k_d, k_s, alpha) + light_reflection_factor(norm, dir, normalize(sun_dir - vec3(0.0, 0.1, 0.0)), k_d, k_s, alpha)*/) + - (1.0 - MOON_AMBIANCE) * moon_chroma * 1.0 * /*4.0 * */light_reflection_factor(norm, dir, moon_dir, k_d, k_s, alpha, voxel_lighting) + (1.0 - MOON_AMBIANCE) * moon_chroma * moon_shadow * 1.0 * /*4.0 * */light_reflection_factor(norm, dir, moon_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting) ); - /* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; - diffuse_light = - sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + - moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + - PERSISTENT_AMBIANCE; - ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); */ + /* light = sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; + diffuse_light = + sun_chroma * mix(1.0, max(dot(-norm, sun_dir) * 0.5 + 0.5, 0.0), diffusion) + + moon_chroma * mix(1.0, pow(dot(-norm, moon_dir) * 2.0, 2.0), diffusion) + + PERSISTENT_AMBIANCE; + ambient_light = vec3(SUN_AMBIANCE * sun_light + moon_light); */ return rel_luminance(emitted_light + reflected_light);//rel_luminance(emitted_light + reflected_light);//sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; } -float get_sun_diffuse2(vec3 norm, vec3 sun_dir, vec3 moon_dir, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, float voxel_lighting, out vec3 emitted_light, out vec3 reflected_light) { - return get_sun_diffuse2(norm, sun_dir, moon_dir, dir, vec3(0.0), vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, voxel_lighting, emitted_light, reflected_light); +float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, vec3 norm, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting, out vec3 emitted_light, out vec3 reflected_light) { + return get_sun_diffuse2(sun_info, moon_info, norm, dir, vec3(0.0), vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, voxel_norm, voxel_lighting, emitted_light, reflected_light); } -float get_sun_diffuse2(vec3 norm, vec3 sun_dir, vec3 moon_dir, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, out vec3 emitted_light, out vec3 reflected_light) { - return get_sun_diffuse2(norm, sun_dir, moon_dir, dir, vec3(0.0), vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, 1.0, emitted_light, reflected_light); +float get_sun_diffuse2(DirectionalLight sun_info, DirectionalLight moon_info, vec3 norm, vec3 dir, vec3 k_a, vec3 k_d, vec3 k_s, float alpha, out vec3 emitted_light, out vec3 reflected_light) { + return get_sun_diffuse2(sun_info, moon_info, norm, dir, vec3(0.0), vec3(0.0), vec3(1.0), 0.0, k_a, k_d, k_s, alpha, norm, 1.0, emitted_light, reflected_light); } // This has been extracted into a function to allow quick exit when detecting a star. float is_star_at(vec3 dir) { - float star_scale = 80.0; + float star_scale = 80.0; - // Star positions - vec3 pos = (floor(dir * star_scale) - 0.5) / star_scale; + // Star positions + vec3 pos = (floor(dir * star_scale) - 0.5) / star_scale; - // Noisy offsets - pos += (3.0 / star_scale) * /*rand_perm_3*/hash(vec4(pos, 1.0)); + // Noisy offsets + pos += (3.0 / star_scale) * /*rand_perm_3*/hash(vec4(pos, 1.0)); - // Find distance to fragment - float dist = length(normalize(pos) - dir); + // Find distance to fragment + float dist = length(normalize(pos) - dir); - // Star threshold - if (dist < 0.0015) { - return 1.0; - } + // Star threshold + if (dist < 0.0015) { + return 1.0; + } - return 0.0; + return 0.0; } vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_stars, float refractionIndex, out vec4 clouds) { - // Sky color - vec3 sun_dir = get_sun_dir(time_of_day); - vec3 moon_dir = get_moon_dir(time_of_day); +#if (CLOUD_MODE == CLOUD_MODE_NONE) + const bool has_clouds = false; +#elif (CLOUD_MODE == CLOUD_MODE_REGULAR) + const bool has_clouds = true; +#endif - // sun_dir = sun_dir.z <= 0 ? refract(sun_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : sun_dir; - // moon_dir = moon_dir.z <= 0 ? refract(moon_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : moon_dir; + if (with_stars || has_clouds) { + // Sky color + /* vec3 sun_dir = get_sun_dir(time_of_day); + vec3 moon_dir = get_moon_dir(time_of_day); */ + vec3 sun_dir = sun_dir.xyz; + vec3 moon_dir = moon_dir.xyz; - // Add white dots for stars. Note these flicker and jump due to FXAA - float star = 0.0; - if (with_stars) { - vec3 star_dir = normalize(sun_dir * dir.z + cross(sun_dir, vec3(0, 1, 0)) * dir.x + vec3(0, 1, 0) * dir.y); - star = is_star_at(star_dir); - } + // sun_dir = sun_dir.z <= 0 ? refract(sun_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : sun_dir; + // moon_dir = moon_dir.z <= 0 ? refract(moon_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : moon_dir; - // Sun - const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0; + // Add white dots for stars. Note these flicker and jump due to FXAA + float star = 0.0; + if (with_stars || has_clouds) { + vec3 star_dir = normalize(sun_dir * dir.z + cross(sun_dir, vec3(0, 1, 0)) * dir.x + vec3(0, 1, 0) * dir.y); + star = is_star_at(star_dir); + } - vec3 sun_halo_color = mix( - SUN_HALO_DUSK, - SUN_HALO_DAY, - max(-sun_dir.z, 0) - ); + // Sun + const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0; - vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * sun_halo_color; - vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.001, 0.0), 3000.0) * SUN_SURF_COLOR * SUN_COLOR_FACTOR; - vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1); + vec3 sun_halo_color = mix( + SUN_HALO_DUSK, + SUN_HALO_DAY, + max(-sun_dir.z, 0) + ); - // Moon - const vec3 MOON_SURF_COLOR = vec3(0.7, 1.0, 1.5) * 500.0; - const vec3 MOON_HALO_COLOR = vec3(0.015, 0.015, 0.05); + vec3 sun_halo = pow(max(dot(dir, -sun_dir) + 0.1, 0.0), 8.0) * sun_halo_color; + vec3 sun_surf = pow(max(dot(dir, -sun_dir) - 0.001, 0.0), 3000.0) * SUN_SURF_COLOR * SUN_COLOR_FACTOR; + vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1); - vec3 moon_halo = pow(max(dot(dir, -moon_dir) + 0.1, 0.0), 8.0) * MOON_HALO_COLOR; - vec3 moon_surf = pow(max(dot(dir, -moon_dir) - 0.001, 0.0), 3000.0) * MOON_SURF_COLOR; - vec3 moon_light = clamp(moon_halo + moon_surf, vec3(0), vec3(max(dir.z * 3.0, 0))); + // Moon + const vec3 MOON_SURF_COLOR = vec3(0.7, 1.0, 1.5) * 500.0; + const vec3 MOON_HALO_COLOR = vec3(0.015, 0.015, 0.05); - // Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1 + vec3 moon_halo = pow(max(dot(dir, -moon_dir) + 0.1, 0.0), 8.0) * MOON_HALO_COLOR; + vec3 moon_surf = pow(max(dot(dir, -moon_dir) - 0.001, 0.0), 3000.0) * MOON_SURF_COLOR; + vec3 moon_light = clamp(moon_halo + moon_surf, vec3(0), vec3(max(dir.z * 3.0, 0))); - vec3 sky_top = mix( - mix( - SKY_DUSK_TOP + star / (1.0 + moon_surf * 100.0), - SKY_NIGHT_TOP + star / (1.0 + moon_surf * 100.0), - max(pow(sun_dir.z, 0.2), 0) - ), - SKY_DAY_TOP, - max(-sun_dir.z, 0) - ); + // Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1 - vec3 sky_mid = mix( - mix( - SKY_DUSK_MID, - SKY_NIGHT_MID, - max(pow(sun_dir.z, 0.2), 0) - ), - SKY_DAY_MID, - max(-sun_dir.z, 0) - ); + vec3 sky_top = mix( + mix( + SKY_DUSK_TOP + star / (1.0 + moon_surf * 100.0), + SKY_NIGHT_TOP + star / (1.0 + moon_surf * 100.0), + max(pow(sun_dir.z, 0.2), 0) + ), + SKY_DAY_TOP, + max(-sun_dir.z, 0) + ); - vec3 sky_bot = mix( - mix( - SKY_DUSK_BOT, - SKY_NIGHT_BOT, - max(pow(sun_dir.z, 0.2), 0) - ), - SKY_DAY_BOT, - max(-sun_dir.z, 0) - ); + vec3 sky_mid = mix( + mix( + SKY_DUSK_MID, + SKY_NIGHT_MID, + max(pow(sun_dir.z, 0.2), 0) + ), + SKY_DAY_MID, + max(-sun_dir.z, 0) + ); - vec3 sky_color = mix( - mix( - sky_mid, - sky_bot, - pow(max(-dir.z, 0), 0.4) - ), - sky_top, - max(dir.z, 0) - ); + vec3 sky_bot = mix( + mix( + SKY_DUSK_BOT, + SKY_NIGHT_BOT, + max(pow(sun_dir.z, 0.2), 0) + ), + SKY_DAY_BOT, + max(-sun_dir.z, 0) + ); - // Approximate distance to fragment - float f_dist = distance(origin, f_pos); + vec3 sky_color = mix( + mix( + sky_mid, + sky_bot, + pow(max(-dir.z, 0), 0.4) + ), + sky_top, + max(dir.z, 0) + ); - // Clouds - clouds = get_cloud_color(dir, origin, time_of_day, f_dist, quality); - clouds.rgb *= get_sun_brightness(sun_dir) * (sun_halo * 1.5 + get_sun_color(sun_dir)) + get_moon_brightness(moon_dir) * (moon_halo * 80.0 + get_moon_color(moon_dir) + 0.25); + // Approximate distance to fragment + float f_dist = distance(origin, f_pos); - if (f_dist > 5000.0) { - sky_color += sun_light + moon_light; - } + // Clouds + #if (CLOUD_MODE == CLOUD_MODE_NONE) + clouds = vec4(0.0); + #elif (CLOUD_MODE == CLOUD_MODE_REGULAR) + clouds = get_cloud_color(dir, origin, time_of_day, f_dist, quality); + clouds.rgb *= get_sun_brightness(/*sun_dir*/) * (sun_halo * 1.5 + get_sun_color(/*sun_dir*/)) + get_moon_brightness(/*moon_dir*/) * (moon_halo * 80.0 + get_moon_color(/*moon_dir*/) + 0.25); + #endif - return mix(sky_color, clouds.rgb, clouds.a); + if (f_dist > 5000.0) { + sky_color += sun_light + moon_light; + } + return mix(sky_color, clouds.rgb, clouds.a); + } else { + clouds = vec4(0.0); + return vec3(0.0); + } } vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_stars, out vec4 clouds) { @@ -400,30 +469,31 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q } float fog(vec3 f_pos, vec3 focus_pos, uint medium) { - return max(1.0 - 5000.0 / (1.0 + distance(f_pos.xy, focus_pos.xy)), 0.0); + return max(1.0 - 5000.0 / (1.0 + distance(f_pos.xy, focus_pos.xy)), 0.0); - float fog_radius = view_distance.x; - float mist_radius = 10000000.0; + float fog_radius = view_distance.x; + float mist_radius = 10000000.0; - float min_fog = 0.5; - float max_fog = 1.0; + float min_fog = 0.5; + float max_fog = 1.0; - if (medium == 1u) { - mist_radius = UNDERWATER_MIST_DIST; - min_fog = 0.0; - } + if (medium == 1u) { + mist_radius = UNDERWATER_MIST_DIST; + min_fog = 0.0; + } - float fog = distance(f_pos.xy, focus_pos.xy) / fog_radius; - float mist = distance(f_pos, focus_pos) / mist_radius; + float fog = distance(f_pos.xy, focus_pos.xy) / fog_radius; + float mist = distance(f_pos, focus_pos) / mist_radius; - return pow(clamp((max(fog, mist) - min_fog) / (max_fog - min_fog), 0.0, 1.0), 1.7); + return pow(clamp((max(fog, mist) - min_fog) / (max_fog - min_fog), 0.0, 1.0), 1.7); } /* vec3 illuminate(vec3 color, vec3 light, vec3 diffuse, vec3 ambience) { - float avg_col = (color.r + color.g + color.b) / 3.0; - return ((color - avg_col) * light + (diffuse + ambience) * avg_col) * (diffuse + ambience); + float avg_col = (color.r + color.g + color.b) / 3.0; + return ((color - avg_col) * light + (diffuse + ambience) * avg_col) * (diffuse + ambience); } */ vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted, vec3 reflected) { + return emitted + reflected; const float NIGHT_EXPOSURE = 10.0; const float DUSK_EXPOSURE = 2.0;//0.8; const float DAY_EXPOSURE = 1.0;//0.7; @@ -446,25 +516,25 @@ vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted float lum = rel_luminance(color); // float lum_sky = lum - max_light; - vec3 sun_dir = get_sun_dir(time_of_day.x); - vec3 moon_dir = get_moon_dir(time_of_day.x); + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ float sky_light = rel_luminance( - get_sun_color(sun_dir) * get_sun_brightness(sun_dir) * SUN_COLOR_FACTOR + - get_moon_color(moon_dir) * get_moon_brightness(moon_dir)); + get_sun_color(/*sun_dir*/) * get_sun_brightness(/*sun_dir*/) * SUN_COLOR_FACTOR + + get_moon_color(/*moon_dir*/) * get_moon_brightness(/*moon_dir*/)); // Tone mapped value. // vec3 T = /*color*//*lum*/color;//normalize(color) * lum / (1.0 + lum); // float alpha = 0.5;//2.0; float alpha = mix( - mix( - DUSK_EXPOSURE, - NIGHT_EXPOSURE, - max(sun_dir.z, 0) - ), - DAY_EXPOSURE, - max(-sun_dir.z, 0) - ); - vec3 now_light = moon_dir.z < 0 ? moon_dir : sun_dir; + mix( + DUSK_EXPOSURE, + NIGHT_EXPOSURE, + max(sun_dir.z, 0) + ), + DAY_EXPOSURE, + max(-sun_dir.z, 0) + ); + vec3 now_light = moon_dir.z < 0 ? moon_dir.xyz : sun_dir.xyz; float cos_view_light = dot(-now_light, view_dir); // alpha *= exp(1.0 - cos_view_light); // sky_light *= 1.0 - log(1.0 + view_dir.z); @@ -497,14 +567,14 @@ vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted // Heuristic desaturation // const float s = 0.8; float s = mix( - mix( - DUSK_SATURATION, - NIGHT_SATURATION, - max(sun_dir.z, 0) - ), - DAY_SATURATION, - max(-sun_dir.z, 0) - ); + mix( + DUSK_SATURATION, + NIGHT_SATURATION, + max(sun_dir.z, 0) + ), + DAY_SATURATION, + max(-sun_dir.z, 0) + ); // s = max(s, (max_light) / (1.0 + s)); s = max(s, max_light / (1.0 + max_light)); diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index d2408923a3..7615b587e8 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -2,6 +2,23 @@ // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); +// // NOTE: Automatic in v4.0 +// float +// mip_map_level(in vec2 texture_coordinate) +// { +// // The OpenGL Graphics System: A Specification 4.2 +// // - chapter 3.9.11, equation 3.21 +// +// +// vec2 dx_vtc = dFdx(texture_coordinate); +// vec2 dy_vtc = dFdy(texture_coordinate); +// float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); +// +// +// //return max(0.0, 0.5 * log2(delta_max_sqr) - 1.0); // == log2(sqrt(delta_max_sqr)); +// return 0.5 * log2(delta_max_sqr); // == log2(sqrt(delta_max_sqr)); +// } + //https://gamedev.stackexchange.com/questions/92015/optimized-linear-to-srgb-glsl vec3 srgb_to_linear(vec3 srgb) { bvec3 cutoff = lessThan(srgb, vec3(0.04045)); @@ -53,8 +70,8 @@ float BeckmannDistribution_D(float NdotH, float alpha) { } // Voxel Distribution -float BeckmannDistribution_D_Voxel(vec3 wh, vec3 norm, float alpha) { - vec3 sides = sign(norm); +float BeckmannDistribution_D_Voxel(vec3 wh, vec3 voxel_norm, float alpha) { + vec3 sides = sign(voxel_norm); // vec3 cos_sides_i = /*sides * */sides * norm; // vec3 cos_sides_o = max(sides * view_dir, 0.0); @@ -64,7 +81,7 @@ float BeckmannDistribution_D_Voxel(vec3 wh, vec3 norm, float alpha) { vec3 NdotH2 = NdotH * NdotH; vec3 NdotH2m2 = NdotH2 * alpha * alpha; vec3 k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); - return dot(mix(k_spec, /*cos_sides_o*/vec3(0.0), equal(NdotH, vec3(0.0))), /*cos_sides_i*/abs(norm)); + return dot(mix(k_spec, /*cos_sides_o*/vec3(0.0), equal(NdotH, vec3(0.0))), /*cos_sides_i*/abs(voxel_norm)); // // const float PI = 3.1415926535897932384626433832795; // const vec3 normals[6] = vec3[](vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1)); @@ -100,8 +117,8 @@ float BeckmannDistribution_D_Voxel(vec3 wh, vec3 norm, float alpha) { // return voxel_norm; } -float TrowbridgeReitzDistribution_D_Voxel(vec3 wh, vec3 norm, float alpha) { - vec3 sides = sign(norm); +float TrowbridgeReitzDistribution_D_Voxel(vec3 wh, vec3 voxel_norm, float alpha) { + vec3 sides = sign(voxel_norm); // vec3 cos_sides_i = /*sides * */sides * norm; // vec3 cos_sides_o = max(sides * view_dir, 0.0); @@ -118,7 +135,7 @@ float TrowbridgeReitzDistribution_D_Voxel(vec3 wh, vec3 norm, float alpha) { vec3 e = (1 - NdotH2) / NdotH2m2; vec3 k_spec = 1.0 / (PI * NdotH2m2 * NdotH2 * (1 + e) * (1 + e)); // vec3 k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); - return dot(mix(k_spec, /*cos_sides_o*/vec3(0.0), equal(NdotH, vec3(0.0))), /*cos_sides_i*/abs(norm)); + return dot(mix(k_spec, /*cos_sides_o*/vec3(0.0), equal(NdotH, vec3(0.0))), /*cos_sides_i*/abs(voxel_norm)); } float BeckmannDistribution_Lambda(vec3 norm, vec3 dir, float alpha) { @@ -223,7 +240,7 @@ vec3 FresnelBlend_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_s, flo // http://www.pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models.html#fragment-MicrofacetDistributionPublicMethods-2 // and // http://www.pbr-book.org/3ed-2018/Reflection_Models/Fresnel_Incidence_Effects.html -vec3 FresnelBlend_Voxel_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_s, float alpha, float dist) { +vec3 FresnelBlend_Voxel_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_s, float alpha, vec3 voxel_norm, float dist) { const float PI = 3.1415926535897932384626433832795; alpha = alpha * sqrt(2.0); float cos_wi = /*max(*/dot(-light_dir, norm)/*, 0.0)*/; @@ -233,7 +250,7 @@ vec3 FresnelBlend_Voxel_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_ vec4 AbsNdotL = abs(vec4(light_dir, cos_wi)); vec4 AbsNdotV = abs(vec4(dir, cos_wo)); #else - vec3 sides = sign(norm); + vec3 sides = sign(voxel_norm); vec4 AbsNdotL = vec4(max(-light_dir * sides, 0.0), abs(cos_wi)); vec4 AbsNdotV = vec4(max(dir * sides, 0.0), abs(cos_wo)); #endif @@ -288,8 +305,8 @@ vec3 FresnelBlend_Voxel_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_ } wh = normalize(wh);//mix(normalize(wh), vec3(0.0), equal(light_dir, dir)); float dot_wi_wh = dot(-light_dir, wh); - // float distr = TrowbridgeReitzDistribution_D_Voxel(wh, norm, alpha); - float distr = BeckmannDistribution_D_Voxel(wh, norm, alpha); + // float distr = TrowbridgeReitzDistribution_D_Voxel(wh, voxel_norm, alpha); + float distr = BeckmannDistribution_D_Voxel(wh, voxel_norm, alpha); // float distr = BeckmannDistribution_D(dot(wh, norm), alpha); vec3 specular = distr / (4 * abs(dot_wi_wh) * @@ -365,67 +382,67 @@ vec3 light_reflection_factor2(vec3 norm, vec3 dir, vec3 light_dir, vec3 k_d, vec // // return vec3(0.0); } -vec3 light_reflection_factor(vec3 norm, vec3 dir, vec3 light_dir, vec3 k_d, vec3 k_s, float alpha, float voxel_lighting) { +vec3 light_reflection_factor(vec3 norm, vec3 dir, vec3 light_dir, vec3 k_d, vec3 k_s, float alpha, vec3 voxel_norm, float voxel_lighting) { #if (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_LAMBERTIAN) const float PI = 3.141592; -#if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) -#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + #if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 vec4 AbsNdotL = abs(vec4(light_dir, dot(norm, light_dir))); -#else - vec3 sides = sign(norm); + #else + vec3 sides = sign(voxel_norm); vec4 AbsNdotL = max(vec4(-light_dir * sides, dot(norm, -light_dir)), 0.0); -#endif - float diffuse = dot(AbsNdotL, vec4(abs(norm) * (1.0 - voxel_lighting), voxel_lighting)); -#elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) -#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + #endif + float diffuse = dot(AbsNdotL, vec4(abs(voxel_norm) * (1.0 - voxel_lighting), voxel_lighting)); + #elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 float diffuse = abs(dot(norm, light_dir)); -#else + #else float diffuse = max(dot(norm, -light_dir), 0.0); -#endif -#endif + #endif + #endif return k_d / PI * diffuse; #elif (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_BLINN_PHONG) const float PI = 3.141592; alpha = alpha * sqrt(2.0); -#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 float ndotL = abs(dot(norm, light_dir)); -#else + #else float ndotL = max(dot(norm, -light_dir), 0.0); -#endif + #endif if (ndotL > 0.0) { -#if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) -#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + #if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 vec4 AbsNdotL = abs(vec4(light_dir, ndotL)); -#else - vec3 sides = sign(norm); + #else + vec3 sides = sign(voxel_norm); vec4 AbsNdotL = max(vec4(-light_dir * sides, ndotL), 0.0); -#endif - float diffuse = dot(AbsNdotL, vec4(abs(norm) * (1.0 - voxel_lighting), voxel_lighting)); -#elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) + #endif + float diffuse = dot(AbsNdotL, vec4(abs(voxel_norm) * (1.0 - voxel_lighting), voxel_lighting)); + #elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) float diffuse = ndotL; -#endif + #endif vec3 H = normalize(-light_dir + dir); -#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 float NdotH = abs(dot(norm, H)); -#else + #else float NdotH = max(dot(norm, H), 0.0); -#endif + #endif return (1.0 - k_s) / PI * k_d * diffuse + k_s * pow(NdotH, alpha/* * 4.0*/); } return vec3(0.0); #elif (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_ASHIKHMIN) -#if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) - return FresnelBlend_Voxel_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha, voxel_lighting); -#elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) + #if (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_VOXEL) + return FresnelBlend_Voxel_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha, voxel_norm, voxel_lighting); + #elif (LIGHTING_DISTRIBUTION_SCHEME == LIGHTING_DISTRIBUTION_SCHEME_MICROFACET) //if (voxel_lighting < 1.0) { return FresnelBlend_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha); //} else { // return FresnelBlend_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s, alpha); //} -#endif + #endif #endif } @@ -530,3 +547,68 @@ vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_a return exp(-mu * sqrt(depth2)); #endif } + +//#ifdef HAS_SHADOW_MAPS +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +//uniform sampler2DShadow t_directed_shadow_maps; +//// uniform sampler2DArrayShadow t_directed_shadow_maps; +// +//float ShadowCalculationDirected(in vec4 /*light_pos[2]*/sun_pos, uint lightIndex) +//{ +// float bias = 0.0;//-0.0001;// 0.05 / (2.0 * view_distance.x); +// // const vec3 sampleOffsetDirections[20] = vec3[] +// // ( +// // vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), +// // vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), +// // vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), +// // vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), +// // vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) +// // // vec3(0, 0, 0) +// // ); +// /* if (lightIndex >= light_shadow_count.z) { +// return 1.0; +// } */ +// // vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz; +// float visibility = textureProj(t_directed_shadow_maps, sun_pos); +// // float visibility = textureProj(t_directed_shadow_maps, vec4(fragPos.xy, /*lightIndex, */fragPos.z + bias, sun_pos.w)); +// return visibility; +// // return mix(visibility, 0.0, sun_pos.z < -1.0); +// // return mix(mix(0.0, 1.0, visibility == 1.0), 1.0, sign(sun_pos.w) * sun_pos.z > /*1.0*/abs(sun_pos.w)); +// // return visibility == 1.0 ? 1.0 : 0.0; +// /* if (visibility == 1.0) { +// return 1.0; +// } */ +// // return visibility; +// /* if (fragPos.z > 1.0) { +// return 1.0; +// } */ +// // if (visibility <= 0.75) { +// // return 0.0; +// // } +// // int samples = 20; +// // float shadow = 0.0; +// // // float bias = 0.0001; +// // float viewDistance = length(cam_pos.xyz - fragPos); +// // // float diskRadius = 0.2 * (1.0 + (viewDistance / screen_res.w)) / 25.0; +// // float diskRadius = 0.0008;//0.005;// / (2.0 * view_distance.x);//(1.0 + (viewDistance / screen_res.w)) / 25.0; +// // for(int i = 0; i < samples; ++i) +// // { +// // vec3 currentDepth = fragPos + vec3(sampleOffsetDirections[i].xyz) * diskRadius + bias; +// // visibility = texture(t_directed_shadow_maps, vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/); +// // shadow += mix(visibility, 1.0, visibility >= 0.5); +// // } +// // shadow /= float(samples); +// // return shadow; +//} +// #elif (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_CHEAP) +//float ShadowCalculationDirected(in vec4 light_pos[2], uint lightIndex) +//{ +// return 1.0; +//} +// #endif +//#else +//float ShadowCalculationDirected(in vec4 light_pos[2], uint lightIndex) +//{ +// return 1.0; +//} +//#endif diff --git a/assets/voxygen/shaders/light-shadows-directed-frag.glsl b/assets/voxygen/shaders/light-shadows-directed-frag.glsl new file mode 100644 index 0000000000..e201b6ed5e --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-directed-frag.glsl @@ -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 + +#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 +// // Currently, we only need lights for the light position +// #include + +// 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 + } +} diff --git a/assets/voxygen/shaders/light-shadows-directed-vert.glsl b/assets/voxygen/shaders/light-shadows-directed-vert.glsl new file mode 100644 index 0000000000..ad50fe5ad4 --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-directed-vert.glsl @@ -0,0 +1,63 @@ +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#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 + +#define HAS_SHADOW_MAPS + +// Currently, we only need globals for focus_off. +#include +// For shadow locals. +#include + +/* Accurate packed shadow maps for many lights at once! + * + * Ideally, we would just write to a bitmask... + * + * */ + +in uint v_pos_norm; +// in uint v_col_light; +// in vec4 v_pos; + +// Light projection matrices. +layout (std140) +uniform u_locals { + vec3 model_offs; + float load_time; + ivec4 atlas_offs; +}; + +// out vec4 shadowMapCoord; + +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; + // 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 * */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 +} diff --git a/assets/voxygen/shaders/light-shadows-figure-vert.glsl b/assets/voxygen/shaders/light-shadows-figure-vert.glsl new file mode 100644 index 0000000000..b7d9d1a21f --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-figure-vert.glsl @@ -0,0 +1,74 @@ +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#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 + +#define HAS_SHADOW_MAPS + +// Currently, we only need globals for focus_off. +#include +// For shadow locals. +#include + +/* Accurate packed shadow maps for many lights at once! + * + * Ideally, we would just write to a bitmask... + * + * */ + +in uint v_pos_norm; +in uint v_atlas_pos; +// in uint v_col_light; +// in vec4 v_pos; + +layout (std140) +uniform u_locals { + mat4 model_mat; + vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; + // bit 0 - is player + // bit 1-31 - unused + int flags; +}; + +struct BoneData { + mat4 bone_mat; + mat4 normals_mat; +}; + +layout (std140) +uniform u_bones { + // Warning: might not actually be 16 elements long. Don't index out of bounds! + BoneData bones[16]; +}; + +// 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; + + vec3 f_pos = ( + bones[bone_idx].bone_mat * + vec4(pos, 1.0) + ).xyz + (model_pos - focus_off.xyz/* + vec3(0.0, 0.0, 0.0001)*/); + + gl_Position = shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos, 1.0); +#endif +} diff --git a/assets/voxygen/shaders/light-shadows-frag.glsl b/assets/voxygen/shaders/light-shadows-frag.glsl index 1c954f97a6..d181836cdc 100644 --- a/assets/voxygen/shaders/light-shadows-frag.glsl +++ b/assets/voxygen/shaders/light-shadows-frag.glsl @@ -21,12 +21,12 @@ #define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN -// Currently, we only need globals for the far plane. -#include -// Currently, we only need lights for the light position -#include +// // Currently, we only need globals for the far plane. +// #include +// // Currently, we only need lights for the light position +// #include -in vec3 FragPos; // FragPos from GS (output per emitvertex) +// in vec3 FragPos; // FragPos from GS (output per emitvertex) // flat in int FragLayer; void main() diff --git a/assets/voxygen/shaders/light-shadows-geom.glsl b/assets/voxygen/shaders/light-shadows-geom.glsl index 8d18e190a9..bda8d778cd 100644 --- a/assets/voxygen/shaders/light-shadows-geom.glsl +++ b/assets/voxygen/shaders/light-shadows-geom.glsl @@ -21,11 +21,14 @@ #define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN +#define HAS_SHADOW_MAPS + // Currently, we only need globals for the max light count (light_shadow_count.x) // and the far plane (scene_res.z). #include -// Currently, we only need lights for the light position -#include +#include +// // Currently, we only need lights for the light position +// #include // Since our output primitive is a triangle strip, we have to render three vertices // each. @@ -171,14 +174,15 @@ layout (triangles/*, invocations = 6*/) in; layout (triangle_strip, max_vertices = /*MAX_LAYER_VERTICES_PER_FACE*//*96*/18) out; -struct ShadowLocals { - mat4 shadowMatrices; -}; - -layout (std140) -uniform u_light_shadows { - ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; -}; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; // NOTE: We choose not to output FragPos currently to save on space limitations // (see extensive documentation above). However, as these limitations have been @@ -218,15 +222,17 @@ void main() { /* if (light_shadow_count.x == 1) { return; } */ - for (int layer = 1; layer <= /*light_shadow_count.x*/1; ++layer) +#if (SHADOW_MODE == SHADOW_MODE_MAP) + for (uint layer = 1u; layer <= min(light_shadow_count.x, 1u); ++layer) { - int layer_base = layer * FACES_PER_POINT_LIGHT; + int layer_base = int(layer) * FACES_PER_POINT_LIGHT; // We use instancing here in order to increase the number of emitted vertices. // int face = gl_InvocationID; for(int face = 0; face < FACES_PER_POINT_LIGHT; ++face) { // int layer_face = layer * FACES_PER_POINT_LIGHT + face; // int layer_face = layer * FACES_PER_POINT_LIGHT + face; + // for(int i = VERTICES_PER_FACE - 1; i >= 0; --i) // for each triangle vertex for(int i = 0; i < VERTICES_PER_FACE; ++i) // for each triangle vertex { // NOTE: See above, we don't make FragPos a uniform. @@ -256,4 +262,5 @@ void main() { EndPrimitive(); } } +#endif } diff --git a/assets/voxygen/shaders/light-shadows-vert.glsl b/assets/voxygen/shaders/light-shadows-vert.glsl index 5e2bc31004..4ba3c77f9d 100644 --- a/assets/voxygen/shaders/light-shadows-vert.glsl +++ b/assets/voxygen/shaders/light-shadows-vert.glsl @@ -17,7 +17,7 @@ #define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN -// Currently, we only need globals for the all_mat matrix. +// Currently, we only need globals for focus_off. #include /* Accurate packed shadow maps for many lights at once! @@ -35,6 +35,7 @@ layout (std140) uniform u_locals { vec3 model_offs; float load_time; + ivec4 atlas_offs; }; // out vec4 shadowMapCoord; @@ -43,7 +44,7 @@ const int EXTRA_NEG_Z = 32768; 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; + vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; // f_pos = v_pos; // vec3 f_pos = f_chunk_pos + model_offs; diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl index 40602613cd..038c4b3892 100644 --- a/assets/voxygen/shaders/lod-terrain-frag.glsl +++ b/assets/voxygen/shaders/lod-terrain-frag.glsl @@ -12,7 +12,8 @@ #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE #endif -#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_VOXEL +// #define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_VOXEL +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET #define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN @@ -22,36 +23,45 @@ in vec3 f_pos; in vec3 f_norm; -in vec2 v_pos_orig; +// in vec2 v_pos_orig; // in vec4 f_shadow; // in vec4 f_square; 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)*/); + #include void main() { + // tgt_color = vec4(vec3(1.0), 1.0); + // return; // vec3 f_pos = lod_pos(f_pos.xy); - // vec3 f_col = lod_col(f_pos.xy); + // vec3 f_col = lod_col(f_pos.xy); // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); float my_alt = /*f_pos.z;*/alt_at_real(f_pos.xy); // vec3 f_pos = vec3(f_pos.xy, max(my_alt, f_pos.z)); - /* gl_Position = - proj_mat * - view_mat * - vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); */ + /* gl_Position = + proj_mat * + view_mat * + vec4(f_pos, 1); + gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); */ vec3 my_pos = vec3(f_pos.xy, my_alt); - vec3 my_norm = lod_norm(f_pos.xy/*, f_square*/); + vec3 my_norm = lod_norm(f_pos.xy/*, f_square*/); float which_norm = dot(my_norm, normalize(cam_pos.xyz - my_pos)); // which_norm = 0.5 + which_norm * 0.5; - which_norm = pow(max(0.0, which_norm), /*0.03125*/1 / 8.0);// * 0.5; + + // which_norm = pow(max(0.0, which_norm), /*0.03125*/1 / 8.0);// * 0.5; + // smoothstep + which_norm = which_norm * which_norm * (3 - 2 * abs(which_norm)); + // which_norm = mix(0.0, 1.0, which_norm > 0.0); - // 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)); + // 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)); vec3 f_norm = mix(faceforward(f_norm, cam_pos.xyz - f_pos, -f_norm), my_norm, which_norm); vec3 f_pos = mix(f_pos, my_pos, which_norm); // vec3 fract_pos = fract(f_pos); @@ -73,7 +83,7 @@ void main() { f_norm = my_f_norm; depth = my_f_proj.z; } - } + } // f_pos = new_f_pos.xyz; } */ @@ -94,7 +104,10 @@ void main() { // mat4 invfoo = foo * inverse(foo * all_mat); // vec3 old_coord = all_mat * vec4(f_pos.xyz, 1.0); // vec4 new_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0); - vec3 f_col = lod_col(f_pos.xy); + vec3 f_col = lod_col(f_pos.xy); + // tgt_color = vec4(f_col, 1.0); + // return; + // vec3 f_col = srgb_to_linear(vec3(1.0)); // vec3 f_norm = faceforward(f_norm, cam_pos.xyz - f_pos, -f_norm); // vec3 f_up = faceforward(cam_pos.xyz - f_pos, vec3(0.0, 0.0, -1.0), cam_pos.xyz - f_pos); // vec3 f_norm = faceforward(f_norm, /*vec3(cam_pos.xyz - f_pos.xyz)*/vec3(0.0, 0.0, -1.0), f_norm); @@ -168,7 +181,7 @@ void main() { // } // voxel_norm = normalize(voxel_norm); */ - float dist_lerp = clamp(pow(max(distance(focus_pos.xy, f_pos.xy) - view_distance.x, 0.0) / 4096.0, 2.0), 0, 1); + float dist_lerp = 0.0;//clamp(pow(max(distance(focus_pos.xy, f_pos.xy) - view_distance.x, 0.0) / 4096.0, 2.0), 0, 1); // dist_lerp = 0.0; // voxel_norm = normalize(mix(voxel_norm, f_norm, /*pow(dist_lerp, 1.0)*/dist_lerp)); @@ -198,9 +211,23 @@ void main() { // we travel along x and y? // // TODO: Handle negative numbers. - vec3 delta_sides = mix(-fract(f_pos), 1.0 - fract(f_pos), lessThan(sides, vec3(0.0))); + // vec3 delta_sides = mix(-fract(f_pos), 1.0 - fract(f_pos), lessThan(sides, vec3(0.0))); + vec3 delta_sides = mix(fract(f_pos) - 1.0, fract(f_pos), lessThan(sides, vec3(0.0))); + /* vec3 delta_sides = + mix( + mix(-fract(f_pos), 1.0 - fract(f_pos), lessThan(sides, vec3(0.0))), + mix(-(f_pos - ceil(f_pos)), 1.0 - (f_pos - ceil(f_pos)), lessThan(sides, vec3(0.0))), + lessThan(f_pos, vec3(0.0)) + ); */ + /* vec3 delta_sides = + mix( + mix(1.0 - fract(f_pos), -fract(f_pos), lessThan(sides, vec3(0.0))), + mix(1.0 - (f_pos - ceil(f_pos)), -(f_pos - ceil(f_pos)), lessThan(sides, vec3(0.0))), + lessThan(f_pos, vec3(0.0)) + ); */ // vec3 delta_sides = mix(1.0 - fract(f_pos), -fract(f_pos), lessThan(sides, vec3(0.0))); // vec3 delta_sides = 1.0 + sides * fract(-sides * f_pos); + // delta_sides = -sign(delta_sides) * (1.0 - abs(delta_sides)); // Three faces: xy, xz, and yz. // TODO: Handle zero slopes (for xz and yz). vec2 corner_xy = min(abs(f_norm.xy / f_norm.z * delta_sides.z), 1.0); @@ -209,22 +236,96 @@ void main() { // vec3 corner_delta = vec3(voxel_norm.xy / voxel_norm.z * delta_sides.z, delta_sides.z); // Now we just compute an (upper bounded) distance to the corner in each direction. // vec3 corner_distance = min(abs(corner_delta), 1.0); + // Now, if both sides hit something, lerp to 0.25. If one side hits something, lerp to 0.75. And if no sides hit something, + // lerp to 1.0 (TODO: incorporate the corner properly). + // Bilinear interpolation on each plane: + float ao_xy = dot(vec2(corner_xy.x, 1.0 - corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.25 : 0.5 : corner_xy.y < 1.00 ? 0.5 : 0.75, corner_xy.x < 1.00 ? 0.75 : 1.00), vec2(corner_xy.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(corner_xy.y, 1.0 - corner_xy.y)); + float ao_yz = dot(vec2(corner_yz.x, 1.0 - corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.25 : 0.5 : corner_yz.y < 1.00 ? 0.5 : 0.75, corner_yz.x < 1.00 ? 0.75 : 1.00), vec2(corner_yz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(corner_yz.y, 1.0 - corner_yz.y)); + float ao_xz = dot(vec2(corner_xz.x, 1.0 - corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.25 : 0.5 : corner_xz.y < 1.00 ? 0.5 : 0.75, corner_xz.x < 1.00 ? 0.75 : 1.00), vec2(corner_xz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(corner_xz.y, 1.0 - corner_xz.y)); + /* float ao_xy = dot(vec2(1.0 - corner_xy.x, corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.25 : 0.5 : corner_xy.y < 1.00 ? 0.5 : 0.75, corner_xy.x < 1.00 ? 0.75 : 1.00), vec2(corner_xy.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(1.0 - corner_xy.y, corner_xy.y)); + float ao_yz = dot(vec2(1.0 - corner_yz.x, corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.25 : 0.5 : corner_yz.y < 1.00 ? 0.5 : 0.75, corner_yz.x < 1.00 ? 0.75 : 1.00), vec2(corner_yz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(1.0 - corner_yz.y, corner_yz.y)); + float ao_xz = dot(vec2(1.0 - corner_xz.x, corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.25 : 0.5 : corner_xz.y < 1.00 ? 0.5 : 0.75, corner_xz.x < 1.00 ? 0.75 : 1.00), vec2(corner_xz.y < 1.00 ? 0.75 : 1.0, 1.0)) * vec2(1.0 - corner_xz.y, corner_xz.y)); */ // Now, if both sides hit something, lerp to 0.0. If one side hits something, lerp to 0.4. And if no sides hit something, // lerp to 1.0. // Bilinear interpolation on each plane: - float ao_xy = dot(vec2(1.0 - corner_xy.x, corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.0 : 0.25 : corner_xy.y < 1.00 ? 0.25 : 1.0, corner_xy.x < 1.00 ? 0.25 : 1.0), vec2(corner_xy.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_xy.y, corner_xy.y)); - float ao_yz = dot(vec2(1.0 - corner_yz.x, corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.0 : 0.25 : corner_yz.y < 1.00 ? 0.25 : 1.0, corner_yz.x < 1.00 ? 0.25 : 1.0), vec2(corner_yz.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_yz.y, corner_yz.y)); - float ao_xz = dot(vec2(1.0 - corner_xz.x, corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.0 : 0.25 : corner_xz.y < 1.00 ? 0.25 : 1.0, corner_xz.x < 1.00 ? 0.25 : 1.0), vec2(corner_xz.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_xz.y, corner_xz.y)); + // float ao_xy = dot(vec2(1.0 - corner_xy.x, corner_xy.x), mat2(vec2(corner_xy.x < 1.00 ? corner_xy.y < 1.00 ? 0.0 : 0.25 : corner_xy.y < 1.00 ? 0.25 : 1.0, corner_xy.x < 1.00 ? 0.25 : 1.0), vec2(corner_xy.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_xy.y, corner_xy.y)); + // float ao_yz = dot(vec2(1.0 - corner_yz.x, corner_yz.x), mat2(vec2(corner_yz.x < 1.00 ? corner_yz.y < 1.00 ? 0.0 : 0.25 : corner_yz.y < 1.00 ? 0.25 : 1.0, corner_yz.x < 1.00 ? 0.25 : 1.0), vec2(corner_yz.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_yz.y, corner_yz.y)); + // float ao_xz = dot(vec2(1.0 - corner_xz.x, corner_xz.x), mat2(vec2(corner_xz.x < 1.00 ? corner_xz.y < 1.00 ? 0.0 : 0.25 : corner_xz.y < 1.00 ? 0.25 : 1.0, corner_xz.x < 1.00 ? 0.25 : 1.0), vec2(corner_xz.y < 1.00 ? 0.25 : 1.0, 1.0)) * vec2(1.0 - corner_xz.y, corner_xz.y)); // Now, multiply each component by the face "share" which is just the absolute value of its normal for that plane... // vec3 f_ao_vec = mix(abs(vec3(ao_yz, ao_xz, ao_xy)), vec3(1.0), bvec3(f_norm.yz == vec2(0.0), f_norm.xz == vec2(0.0), f_norm.xy == vec2(0.0))); // vec3 f_ao_vec = mix(abs(vec3(ao_yz, ao_xz, ao_xy)), vec3(1.0), bvec3(length(f_norm.yz) <= 0.0, length(f_norm.xz) <= 0.0, length(f_norm.xy) <= 0.0)); // vec3 f_ao_vec = mix(abs(vec3(ao_yz, ao_xz, ao_xy)), vec3(1.0), bvec3(abs(f_norm.x) <= 0.0, abs(f_norm.y) <= 0.0, abs(f_norm.z) <= 0.0)); vec3 f_ao_vec = mix(/*abs(voxel_norm)*/vec3(1.0, 1.0, 1.0), /*abs(voxel_norm) * */vec3(ao_yz, ao_xz, ao_xy), /*abs(voxel_norm)*/vec3(length(f_norm.yz), length(f_norm.xz), length(f_norm.xy))/*vec3(1.0)*//*sign(max(view_dir * sides, 0.0))*/); - // f_ao_vec *= sign(max(view_dir * sides, 0.0)); - // vec3 f_ao_view = max(vec3(dot(view_dir.yz, sides.yz), dot(view_dir.xz, sides.xz), dot(view_dir.xy, sides.xy)), 0.0); + float f_orig_len = length(cam_pos.xyz - f_pos); + vec3 f_orig_view_dir = normalize(cam_pos.xyz - f_pos); + // f_ao_vec *= sign(max(f_orig_view_dir * sides, 0.0)); + // Projecting view onto face: + // bool IntersectRayPlane(vec3 rayOrigin, vec3 rayDirection, vec3 posOnPlane, vec3 planeNormal, inout vec3 intersectionPoint) + // { + // float rDotn = dot(rayDirection, planeNormal); + // + // //parallel to plane or pointing away from plane? + // if (rDotn < 0.0000001 ) + // return false; + // + // float s = dot(planeNormal, (posOnPlane - rayOrigin)) / rDotn; + // + // intersectionPoint = rayOrigin + s * rayDirection; + // + // return true; + // } + + bvec3 hit_yz_xz_xy; + vec3 dist_yz_xz_xy; + /* { + // vec3 rDotn = -f_orig_view_dir * -sides; + // vec3 rDotn = f_orig_view_dir * sides; + // hit_yz_xz_xy = greaterThanEqual(rDotn, vec3(0.000001)); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / rDotn; + // dist_yz_xz_xy = abs(s * -f_orig_view_dir); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / (-f_orig_view_dir * -sides); + // vec3 s = (f_pos + delta_sides - cam_pos.xyz) / -f_orig_view_dir; + // dist_yz_xz_xy = abs(s); + hit_yz_xz_xy = greaterThanEqual(f_orig_view_dir * sides, vec3(0.000001)); + dist_yz_xz_xy = abs((f_pos + delta_sides - cam_pos.xyz) / -f_orig_view_dir); + } */ + { + // vec3 rDotn = -f_orig_view_dir * -sides; + // vec3 rDotn = f_orig_view_dir * sides; + // hit_yz_xz_xy = greaterThanEqual(rDotn, vec3(0.000001)); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / rDotn; + // dist_yz_xz_xy = abs(s * -f_orig_view_dir); + // vec3 s = -sides * (f_pos + delta_sides - cam_pos.xyz) / (-f_orig_view_dir * -sides); + // vec3 s = (f_pos + delta_sides - cam_pos.xyz) / -f_orig_view_dir; + // dist_yz_xz_xy = abs(s); + hit_yz_xz_xy = greaterThanEqual(f_orig_view_dir * sides, vec3(0.000001)); + dist_yz_xz_xy = abs((f_pos + delta_sides - cam_pos.xyz) / f_orig_view_dir); + } + + // vec3 xy_point = f_pos, xz_point = f_pos, yz_point = f_pos; + // bool hit_xy = (/*ao_yz < 1.0 || ao_xz < 1.0*//*min(f_ao_vec.x, f_ao_vec.y)*//*f_ao_vec.z < 1.0*/true/*min(corner_xz.y, corner_yz.y) < 1.0*//*min(corner_xy.x, corner_xy.y) < 1.0*/) && IntersectRayPlane(cam_pos.xyz, -f_orig_view_dir, vec3(f_pos.x, f_pos.y, f_pos.z + delta_sides.z/* - sides.z*/), vec3(0.0, 0.0, -sides.z), xy_point); + // bool hit_xz = (/*ao_xy < 1.0 || ao_yz < 1.0*//*min(f_ao_vec.x, f_ao_vec.z)*//*f_ao_vec.y < 1.0*/true/*min(corner_xy.y, corner_yz.x) < 1.0*//*min(corner_xz.x, corner_xz.y) < 1.0*/) && IntersectRayPlane(cam_pos.xyz, -f_orig_view_dir, vec3(f_pos.x, f_pos.y + delta_sides.y/* - sides.y*/, f_pos.z), vec3(0.0, -sides.y, 0.0), xz_point); + // bool hit_yz = (/*ao_xy < 1.0 || ao_xz < 1.0*//*min(f_ao_vec.y, f_ao_vec.z) < 1.0*//*f_ao_vec.x < 1.0*/true/*true*//*min(corner_xy.x, corner_xz.x) < 1.0*//*min(corner_yz.x, corner_yz.y) < 1.0*/) && IntersectRayPlane(cam_pos.xyz, -f_orig_view_dir, vec3(f_pos.x + delta_sides.x/* - sides.x*/, f_pos.y, f_pos.z), vec3(-sides.x, 0.0, 0.0), yz_point); + // float xy_dist = distance(cam_pos.xyz, xy_point), xz_dist = distance(cam_pos.xyz, xz_point), yz_dist = distance(cam_pos.xyz, yz_point); + bool hit_xy = hit_yz_xz_xy.z, hit_xz = hit_yz_xz_xy.y, hit_yz = hit_yz_xz_xy.x; + float xy_dist = dist_yz_xz_xy.z, xz_dist = dist_yz_xz_xy.y, yz_dist = dist_yz_xz_xy.x; + // hit_xy = hit_xy && distance(f_pos.xy + delta_sides.xy, xy_point.xy) <= 1.0; + // hit_xz = hit_xz && distance(f_pos.xz + delta_sides.xz, xz_point.xz) <= 1.0; + // hit_yz = hit_yz && distance(f_pos.yz + delta_sides.yz, yz_point.yz) <= 1.0; + vec3 voxel_norm = + hit_yz ? + hit_xz ? + yz_dist < xz_dist ? + hit_xy ? yz_dist < xy_dist ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + hit_xy ? xz_dist < xy_dist ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + hit_xy ? yz_dist < xy_dist ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + hit_xz ? + hit_xy ? xz_dist < xy_dist ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + hit_xy ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); + // vec3 f_ao_view = max(vec3(dot(f_orig_view_dir.yz, sides.yz), dot(f_orig_view_dir.xz, sides.xz), dot(f_orig_view_dir.xy, sides.xy)), 0.0); // delta_sides *= sqrt(1.0 - f_ao_view * f_ao_view); // delta_sides *= 1.0 - mix(view_dir / f_ao_view, vec3(0.0), equal(f_ao_view, vec3(0.0)));// sqrt(1.0 - f_ao_view * f_ao_view); - // delta_sides *= 1.0 - /*sign*/(max(vec3(dot(view_dir.yz, sides.yz), dot(view_dir.xz, sides.xz), dot(view_dir.xy, sides.xy)), 0.0)); + // delta_sides *= 1.0 - /*sign*/(max(vec3(dot(f_orig_view_dir.yz, sides.yz), dot(f_orig_view_dir.xz, sides.xz), dot(f_orig_view_dir.xy, sides.xy)), 0.0)); // f_ao = length(f_ao_vec); // f_ao = dot(f_ao_vec, vec3(1.0)) / 3.0; // f_ao = 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); @@ -256,10 +357,21 @@ void main() { // abs(delta_sides.y) * f_ao_vec.y < abs(delta_sides.z) * f_ao_vec.z ? f_ao_vec.y : f_ao_vec.z; // f_ao = dot(abs(voxel_norm), abs(voxel_norm) * f_ao_vec)/* / 3.0*/; // f_ao = sqrt(dot(abs(voxel_norm), f_ao_vec) / 3.0); - // f_ao = /*abs(sides)*/max(sign(1.0 + view_dir * sides), 0.0) * f_ao); + // f_ao = /*abs(sides)*/max(sign(1.0 + f_orig_view_dir * sides), 0.0) * f_ao); // f_ao = mix(f_ao, 1.0, dist_lerp); // vec3 voxel_norm = f_norm; + // vec3 voxel_norm = + // f_ao_vec.x < 1.0 ? + // f_ao_vec.y < 1.0 ? + // abs(delta_sides.x) < abs(delta_sides.y) ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? vec3(sides.x, 0.0, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(sides.x, 0.0, 0.0) : + // f_ao_vec.y < 1.0 ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : + // f_ao_vec.z < 1.0 ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); + // vec3 voxel_norm = // /*f_ao_vec.x < 1.0*/true ? // /*f_ao_vec.y < 1.0*/true ? @@ -290,16 +402,39 @@ void main() { // f_ao_vec.y < 1.0 ? // f_ao_vec.z < 1.0 ? abs(delta_sides.y) * f_ao_vec.y < abs(delta_sides.z) * f_ao_vec.z ? vec3(0.0, sides.y, 0.0) : vec3(0.0, 0.0, sides.z) : vec3(0.0, sides.y, 0.0) : // f_ao_vec.z < 1.0 ? vec3(0.0, 0.0, sides.z) : vec3(0.0, 0.0, 0.0); - vec3 voxel_norm = vec3(0.0); + // vec3 voxel_norm = vec3(0.0); // voxel_norm = mix(voxel_norm, f_norm, dist_lerp); - f_pos.xyz -= abs(voxel_norm) * delta_sides; + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ + // voxel_norm = vec3(0.0); + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) + float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z); + // float shadow_alt = f_pos.z; +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float shadow_alt = f_pos.z; +#endif + +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) + 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) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir); + + f_pos.xyz += abs(voxel_norm) * delta_sides; voxel_norm = voxel_norm == vec3(0.0) ? f_norm : voxel_norm; + f_col = /*srgb_to_linear*/(f_col + hash(vec4(floor(f_pos * 3.0 - voxel_norm * 0.5), 0)) * 0.01/* - 0.01*/); // Small-scale noise + // f_ao = 1.0; // f_ao = dot(f_ao_vec, sqrt(1.0 - delta_sides * delta_sides)); - f_ao = sqrt(dot(f_ao_vec * abs(voxel_norm), sqrt(1.0 - delta_sides * delta_sides)) / 3.0); + f_ao = dot(f_ao_vec, abs(voxel_norm)); + // f_ao = sqrt(dot(f_ao_vec * abs(voxel_norm), sqrt(1.0 - delta_sides * delta_sides)) / 3.0); // vec3 ao_pos2 = min(fract(f_pos), 1.0 - fract(f_pos)); // f_ao = sqrt(dot(ao_pos2, ao_pos2)); @@ -336,27 +471,31 @@ void main() { // vec3 view_dir = normalize(f_pos - cam_pos.xyz); - 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); - // float my_alt = f_pos.z;//alt_at_real(f_pos.xy); - // vec3 f_norm = my_norm; - vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); - // float my_alt = alt_at(f_pos.xy); - float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy); - float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); - float moon_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir); - // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); - // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */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; - // float brightness_denominator = (ambient_sides + vec3(SUN_AMBIANCE * sun_light + moon_light); + // 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); + // // float my_alt = f_pos.z;//alt_at_real(f_pos.xy); + // // vec3 f_norm = my_norm; + // // vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + // // float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z); + // // float my_alt = alt_at(f_pos.xy); + // float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); + // float moon_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, moon_dir); + // // float sun_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, sun_dir); + // // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */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; + // // float brightness_denominator = (ambient_sides + vec3(SUN_AMBIANCE * sun_light + moon_light); + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, moon_shade_frac/*, light_pos*/); float alpha = 1.0;//0.1;//0.2;///1.0;//sqrt(2.0); const float n2 = 1.5; @@ -368,7 +507,7 @@ void main() { float fluid_alt = medium.x == 1u ? max(cam_alt + 1, floor(shadow_alt)) : view_distance.w; float R_s = (f_pos.z < my_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); - vec3 emitted_light, reflected_light; + vec3 emitted_light, reflected_light; vec3 mu = medium.x == 1u/* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0); // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. @@ -376,14 +515,14 @@ void main() { // Use f_norm here for better shadows. // vec3 light_frac = light_reflection_factor(f_norm/*l_norm*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(/*1.0*/R_s), alpha); - // vec3 light, diffuse_light, ambient_light; + // vec3 light, diffuse_light, ambient_light; // get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, (0.25 * shade_frac + 0.25 * light_frac) * f_col, 0.5 * shade_frac * f_col, 0.5 * shade_frac * /*vec3(1.0)*/f_col, 2.0, emitted_light, reflected_light); float max_light = 0.0; - max_light += get_sun_diffuse2(/*f_norm*/voxel_norm/*l_norm*/, sun_dir, moon_dir, view_dir, f_pos, vec3(0.0), cam_attenuation, fluid_alt, vec3(1.0)/* * (0.5 * light_frac + vec3(0.5 * shade_frac))*/, vec3(1.0), /*0.5 * shade_frac * *//*vec3(1.0)*//*f_col*/vec3(R_s), alpha, dist_lerp/*max(distance(focus_pos.xy, f_pos.xyz) - view_distance.x, 0.0) / 1000 < 1.0*/, emitted_light, reflected_light); + max_light += get_sun_diffuse2(sun_info, moon_info, voxel_norm/*l_norm*/, view_dir, f_pos, vec3(0.0), cam_attenuation, fluid_alt, vec3(1.0)/* * (0.5 * light_frac + vec3(0.5 * shade_frac))*/, vec3(1.0), /*0.5 * shade_frac * *//*vec3(1.0)*//*f_col*/vec3(R_s), alpha, voxel_norm, dist_lerp/*max(distance(focus_pos.xy, f_pos.xyz) - view_distance.x, 0.0) / 1000 < 1.0*/, emitted_light, reflected_light); // emitted_light = vec3(1.0); - emitted_light *= max(shade_frac, MIN_SHADOW); - reflected_light *= shade_frac; - max_light *= shade_frac; + // emitted_light *= max(shade_frac, MIN_SHADOW); + // reflected_light *= shade_frac; + // max_light *= shade_frac; // reflected_light = vec3(0.0); // dot(diffuse_factor, /*R_r * */vec4(abs(norm) * (1.0 - dist), dist)) @@ -406,10 +545,10 @@ void main() { // vec3(0.0, /*2.0 * SAMPLE_W*/square.w - square.y, alty1 - alty0) // )); // vec3 norm = normalize(vec3( - // (altx0 - altx1) / (square.z - square.x), - // (alty0 - alty1) / (square.w - square.y), + // (altx0 - altx1) / (square.z - square.x), + // (alty0 - alty1) / (square.w - square.y), // 1.0 - // //(abs(square.w - square.y) + abs(square.z - square.x)) / (slope + 0.00001) // Avoid NaN + // //(abs(square.w - square.y) + abs(square.z - square.x)) / (slope + 0.00001) // Avoid NaN // )); // // If a side coordinate is 0, then it counts as no AO; @@ -423,32 +562,35 @@ void main() { // f_ao = /*sqrt*/1.0 - (dot(ao_pos, ao_pos)/* / 2.0*/); // f_ao = /*sqrt*/1.0 - 2.0 * (dot(ao_pos, ao_pos)/* / 2.0*/); // f_ao = /*sqrt*/1.0 - 2.0 * sqrt(dot(ao_pos, ao_pos) / 2.0); - float ao = /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1; - emitted_light *= ao; - reflected_light *= ao; + float ao = f_ao;// /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1; + emitted_light *= ao; + reflected_light *= ao; // emitted_light += 0.5 * vec3(SUN_AMBIANCE * sun_shade_frac * sun_light + moon_shade_frac * moon_light) * f_col * (ambient_sides + 1.0); // Ambient lighting attempt: vertical light. // reflected_light += /*0.0125*/0.15 * 0.25 * _col * light_reflection_factor(f_norm, cam_to_frag, vec3(0, 0, -1.0), 0.5 * f_col, 0.5 * f_col, 2.0); // emitted_light += /*0.0125*/0.25 * f_col * ; - // vec3 light, diffuse_light, ambient_light; - // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); - // f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise + // vec3 light, diffuse_light, ambient_light; + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + // f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise vec3 surf_color = /*illuminate(emitted_light, reflected_light)*/illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 1.0, true, clouds); - vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); - // vec3 color = surf_color; +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + vec4 clouds; + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 1.0, /*true*/false, clouds); + vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif - // float mist_factor = max(1 - (f_pos.z + (texture(t_noise, f_pos.xy * 0.0005 + time_of_day.x * 0.0003).x - 0.5) * 128.0) / 400.0, 0.0); - // //float mist_factor = f_norm.z * 2.0; - // color = mix(color, vec3(1.0) * /*diffuse_light*/reflected_light, clamp(mist_factor * 0.00005 * distance(f_pos.xy, focus_pos.xy), 0, 0.3)); + // float mist_factor = max(1 - (f_pos.z + (texture(t_noise, f_pos.xy * 0.0005 + time_of_day.x * 0.0003).x - 0.5) * 128.0) / 400.0, 0.0); + // //float mist_factor = f_norm.z * 2.0; + // color = mix(color, vec3(1.0) * /*diffuse_light*/reflected_light, clamp(mist_factor * 0.00005 * distance(f_pos.xy, focus_pos.xy), 0, 0.3)); // color = surf_color; - tgt_color = vec4(color, 1.0); + tgt_color = vec4(color, 1.0); } diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl index 80ecefa48c..1a144dbe41 100644 --- a/assets/voxygen/shaders/lod-terrain-vert.glsl +++ b/assets/voxygen/shaders/lod-terrain-vert.glsl @@ -29,7 +29,7 @@ uniform u_locals { out vec3 f_pos; out vec3 f_norm; -out vec2 v_pos_orig; +// out vec2 v_pos_orig; // out vec4 f_square; // out vec4 f_shadow; // out float f_light; @@ -40,7 +40,7 @@ void main() { vec2 dims = vec2(1.0 / view_distance.y); vec4 f_square = focus_pos.xyxy + vec4(splay(v_pos - dims), splay(v_pos + dims)); f_norm = lod_norm(f_pos.xy, f_square); - v_pos_orig = v_pos; + // v_pos_orig = v_pos; // f_pos = lod_pos(focus_pos.xy + splay(v_pos) * /*1000000.0*/(1 << 20), square); @@ -92,10 +92,13 @@ void main() { // f_light = 1.0; gl_Position = - proj_mat * - view_mat * + /* proj_mat * + view_mat * */ + all_mat * vec4(f_pos/*newRay*/, 1); // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z * gl_Position.w; // gl_Position.z = -gl_Position.z / 100.0; - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/player-shadow-frag.glsl b/assets/voxygen/shaders/player-shadow-frag.glsl index f1ad8f1e5c..693bc30409 100644 --- a/assets/voxygen/shaders/player-shadow-frag.glsl +++ b/assets/voxygen/shaders/player-shadow-frag.glsl @@ -18,18 +18,21 @@ in vec3 f_pos; in vec3 f_col; flat in vec3 f_norm; in float f_ao; -in float f_alt; -in vec4 f_shadow; +// in float f_alt; +// in vec4 f_shadow; layout (std140) uniform u_locals { mat4 model_mat; vec4 model_col; + ivec4 atlas_offs; + vec3 model_pos; int flags; }; struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -44,17 +47,17 @@ uniform u_bones { out vec4 tgt_color; void main() { - float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; + // float distance = distance(vec3(cam_pos), focus_pos.xyz) - 2; - float opacity = clamp(distance / distance_divider, 0, 1); + // float opacity = clamp(distance / distance_divider, 0, 1); - if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { - discard; - } + // if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > opacity) { + // discard; + // } - if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > shadow_dithering) { - discard; - } + // if(threshold_matrix[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4] > shadow_dithering) { + // discard; + // } tgt_color = vec4(0.0,0.0,0.0, 1.0); } diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index 2dc7e44d45..cb179bfec3 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -144,9 +144,9 @@ vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted void main() { vec2 uv = (f_pos + 1.0) * 0.5; - if (medium.x == 1u) { + /* if (medium.x == 1u) { uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); - } + } */ vec2 c_uv = vec2(0.5);//uv;//vec2(0.5);//uv; vec2 delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*//*0.5 - */min(uv, 1.0 - uv);//min(uv * (1.0 - uv), 0.25) * 2.0; @@ -186,9 +186,11 @@ void main() { vec4 final_color = pow(aa_color, gamma); - /* if (medium.x == 1u) { +#if (FLUID_MODE == FLUID_MODE_CHEAP) + if (medium.x == 1u) { final_color *= vec4(0.2, 0.2, 0.8, 1.0); - } */ + } +#endif tgt_color = vec4(final_color.rgb, 1); } diff --git a/assets/voxygen/shaders/postprocess-vert.glsl b/assets/voxygen/shaders/postprocess-vert.glsl index 8002bc6df0..d88985486b 100644 --- a/assets/voxygen/shaders/postprocess-vert.glsl +++ b/assets/voxygen/shaders/postprocess-vert.glsl @@ -30,5 +30,5 @@ out vec2 f_pos; void main() { f_pos = v_pos; - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } diff --git a/assets/voxygen/shaders/skybox-frag.glsl b/assets/voxygen/shaders/skybox-frag.glsl index 068b31572e..a39977036d 100644 --- a/assets/voxygen/shaders/skybox-frag.glsl +++ b/assets/voxygen/shaders/skybox-frag.glsl @@ -30,6 +30,8 @@ uniform u_locals { out vec4 tgt_color; void main() { + // tgt_color = vec4(MU_SCATTER, 1.0); + // return; vec4 _clouds; vec3 cam_dir = normalize(f_pos - cam_pos.xyz); diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl index 7947ca4ec3..86759a29fe 100644 --- a/assets/voxygen/shaders/skybox-vert.glsl +++ b/assets/voxygen/shaders/skybox-vert.glsl @@ -28,12 +28,22 @@ uniform u_locals { 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 gl_Position = - proj_mat * - view_mat * - vec4(v_pos * 100000.0 + cam_pos.xyz, 1); - gl_Position.z = 0.0; + /* proj_mat * + view_mat * */ + all_mat * + /* 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 = gl_Position.w; + // gl_Position.z = gl_Position.w - 0.000001;//0.0; + // gl_Position.z = 1.0; + // gl_Position.z = -1.0; } diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl index 4b27cb9c54..fd97f688ac 100644 --- a/assets/voxygen/shaders/sprite-frag.glsl +++ b/assets/voxygen/shaders/sprite-frag.glsl @@ -12,15 +12,32 @@ #define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN -// #define HAS_SHADOW_MAPS +#define HAS_SHADOW_MAPS #include in vec3 f_pos; flat in vec3 f_norm; -in vec3 f_col; -in float f_ao; +flat in float f_light; +// flat in vec3 f_pos_norm; +in vec2 f_uv_pos; +// flat in uint f_atlas_pos; +// in vec3 f_col; +// in float f_ao; // in float f_light; +// in vec4 light_pos[2]; + +uniform sampler2D t_col_light; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; out vec4 tgt_color; @@ -31,19 +48,70 @@ out vec4 tgt_color; const float FADE_DIST = 32.0; void main() { + /* if (f_uv_pos.x < 757) { + discard; + } */ + // vec2 f_uv_pos = vec2(768,1) + 0.5; + // vec2 f_uv_pos = vec2(760, 380);// + 0.5; + // vec2 f_uv_pos = vec2((uvec2(f_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)) + 0.5; + /* if (f_uv_pos.x < 757) { + discard; + } */ + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); + + // vec4 f_col_light = texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)/* + uv_delta*//* - f_norm * 0.00001*/); + // vec4 f_col_light = textureGrad(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0), vec2(0.5), vec2(0.5)); + vec4 f_col_light = texelFetch(t_col_light, ivec2(f_uv_pos)/* + uv_delta*//* - f_norm * 0.00001*/, 0); + vec3 f_col = /*linear_to_srgb*//*srgb_to_linear*/(f_col_light.rgb); + // vec3 f_col = vec3(1.0); + // vec2 texSize = textureSize(t_col_light, 0); + // float f_ao = f_col_light.a; + // float f_ao = f_col_light.a + length(vec2(dFdx(f_col_light.a), dFdy(f_col_light.a))); + float f_ao = texture(t_col_light, (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_ao = 1.0; + // float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + + // vec3 my_chunk_pos = 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); + /* 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, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); - float moon_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, moon_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;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + 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). @@ -52,7 +120,12 @@ void main() { // 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; + // 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, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); vec3 surf_color = /*srgb_to_linear*//*linear_to_srgb*/(f_col); float alpha = 1.0; @@ -69,7 +142,6 @@ void main() { vec3 emitted_light, reflected_light; - float point_shadow = shadow_at(f_pos, f_norm); // 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); @@ -85,10 +157,13 @@ void main() { // 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(f_norm, /*time_of_day.x, */sun_dir, moon_dir, /*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; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x, *//*cam_to_frag*/view_dir, k_a * f_light/* * (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; @@ -102,17 +177,23 @@ void main() { emitted_light += point_light; reflected_light += point_light; */ - float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + // float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + float ao = f_ao; emitted_light *= ao; 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); +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif + // tgt_color = vec4(color, 1.0); tgt_color = vec4(color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (sprite_render_distance - FADE_DIST)) / FADE_DIST, 0, 1)); } diff --git a/assets/voxygen/shaders/sprite-vert.glsl b/assets/voxygen/shaders/sprite-vert.glsl index c6a14126a8..2d9cc75b84 100644 --- a/assets/voxygen/shaders/sprite-vert.glsl +++ b/assets/voxygen/shaders/sprite-vert.glsl @@ -14,64 +14,223 @@ #include #include +#include in vec3 v_pos; -in uint v_col; +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 vec3 inst_col; +// in vec3 inst_col; in float inst_wind_sway; +struct SpriteLocals { + mat4 mat; + vec4 wind_sway; + vec4 offs; +}; + +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; +}; + out vec3 f_pos; flat out vec3 f_norm; -out vec3 f_col; -out float f_ao; +flat out float f_light; +// flat out vec3 f_pos_norm; +// out vec3 f_col; +// out float f_ao; +out vec2 f_uv_pos; +// 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; void main() { - mat4 inst_mat; - inst_mat[0] = inst_mat0; - inst_mat[1] = inst_mat1; - inst_mat[2] = inst_mat2; - inst_mat[3] = inst_mat3; + // 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; - vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; + // 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; - f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz; - f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); + // vec3 sprite_pos = floor(inst_mat3.xyz * SCALE) + inst_offs; - // Wind waving - f_pos += 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) * SCALE, 1.3) * 0.2; + // f_pos_norm = v_pos; - // 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)); - f_norm = (inst_mat * vec4(normals[(v_norm_ao >> 0) & 0x7u], 0)).xyz; + // 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; - 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; + // vec3 v_pos = vec3(gl_VertexID * 32, gl_VertexID % 32, 1.0); + // f_pos = v_pos + (model_offs - focus_off.xyz); - // Select glowing - if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) { - f_col *= 4.0; - } + // 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); - // f_light = 1.0; + // 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); - gl_Position = - all_mat * - vec4(f_pos, 1); - // 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); + f_pos = (inst_mat * vec4(v_pos_, 1.0)).xyz * SCALE + inst_offs; + // f_pos = (inst_mat * v_pos_) * SCALE + sprite_pos; + + // 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)); + + // 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 + ) * pow(abs(v_pos_.z/* + sprites[0].offs.z*/)/* * SCALE*/, 1.3) * /*0.2;*/SCALE_FACTOR; + } + + // 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; + + // 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]); + + // 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_light = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 1.0 / PERSISTENT_AMBIANCE : 1.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); } diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl index 6d04158ced..0185456b30 100644 --- a/assets/voxygen/shaders/terrain-frag.glsl +++ b/assets/voxygen/shaders/terrain-frag.glsl @@ -23,13 +23,35 @@ #include in vec3 f_pos; -in vec3 f_chunk_pos; +// in float f_ao; +// in vec3 f_chunk_pos; +// #ifdef FLUID_MODE_SHINY flat in uint f_pos_norm; +// #else +// const uint f_pos_norm = 0u; +// #endif // in float f_alt; // in vec4 f_shadow; -in vec3 f_col; -in float f_light; -in float f_ao; +// in vec3 f_col; +// in float f_light; +/*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)); + +/* #if (SHADOW_MODE == SHADOW_MODE_MAP) +in vec4 sun_pos; +#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +const vec4 sun_pos = vec4(0.0); +#endif */ + +uniform sampler2D t_col_light; + +layout (std140) +uniform u_locals { + vec3 model_offs; + float load_time; + ivec4 atlas_offs; +}; out vec4 tgt_color; @@ -38,6 +60,44 @@ out vec4 tgt_color; #include void main() { + // discard; + // vec4 f_col_light = textureGrad(t_col_light, f_uv_pos / texSize, 0.25, 0.25); + // vec4 f_col_light = texture(t_col_light, (f_uv_pos) / texSize); + + // First 3 normals are negative, next 3 are positive + const vec3 normals[8] = 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), vec3(0,0,0), vec3(0,0,0)); + + // uint norm_index = (f_pos_norm >> 29) & 0x7u; + // vec2 uv_delta = (norm_index & 0u) == 0u ? vec2(-1.0) : vec2(0); + + vec2 f_uv_pos = f_uv_pos + atlas_offs.xy; + // 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; + vec4 f_col_light = texelFetch(t_col_light, ivec2(f_uv_pos)/* + uv_delta*//* - f_norm * 0.00001*/, 0); + // float f_light = f_col_light.a; + // vec4 f_col_light = texelFetch(t_col_light, ivec2(int(f_uv_pos.x), int(f_uv_pos.y)/* + uv_delta*//* - f_norm * 0.00001*/), 0); + vec3 f_col = /*linear_to_srgb*//*srgb_to_linear*/(f_col_light.rgb); + // vec3 f_col = vec3(1.0); + float f_light = texture(t_col_light, (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; + // vec2 texSize = textureSize(t_col_light, 0); + // float f_light = texture(t_col_light, f_uv_pos/* + vec2(atlas_offs.xy)*/).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_light = textureProj(t_col_light, vec3(f_uv_pos/* + vec2(atlas_offs.xy)*/, texSize.x)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_light = textureProjLod(t_col_light, vec3(f_uv_pos/* + vec2(atlas_offs.xy)*/, texSize.x), 0).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // float f_light = textureGrad(t_col_light, (f_uv_pos + 0.5) / texSize, vec2(0.1, 0.0), vec2(0.0, 0.1)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; + // f_light = sqrt(f_light); + // f_light = sqrt(f_light); + // f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // vec3 f_col = light_col.rgb;//vec4(1.0, 0.0, 0.0, 1.0); + + // float f_ao = 1.0; + + // vec3 my_chunk_pos = vec3(ivec3((uvec3(f_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu))); + // 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.rgb *= f_light; + // tgt_color = vec4(vec3(f_light), 1.0); + // tgt_color = vec4(f_col, 1.0); + // return; + // vec4 light_pos[2]; // vec4 light_col = vec4( // hash(floor(vec4(f_pos.x, 0, 0, 0))), // hash(floor(vec4(0, f_pos.y, 0, 1))), @@ -46,7 +106,7 @@ void main() { // ); // vec3 f_col = light_col.rgb;//vec4(1.0, 0.0, 0.0, 1.0); // tgt_color = vec4(f_col, 1.0); - // return; + // tgt_color = vec4(light_shadow_count.x <= 31u ? f_col : vec3(0.0), 1.0); // tgt_color = vec4(0.0, 0.0, 0.0, 1.0); // float sum = 0.0; // for (uint i = 0u; i < /* 6 * */light_shadow_count.x; i ++) { @@ -91,6 +151,18 @@ void main() { // sum += light_strength; // } + // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. + // uint norm_axis = (f_pos_norm >> 30) & 0x3u; + // // Increase array access by 3 to access positive values + // uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; + // Use an array to avoid conditional branching + // uint norm_index = (f_pos_norm >> 29) & 0x7u; + // vec3 f_norm = normals[norm_index]; + vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u]; + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); + // /* if (light_shadow_count.x == 1) { // tgt_color.rgb = vec3(0.0); // } */ @@ -98,16 +170,6 @@ void main() { // tgt_color.rgb /= sum; // } // return; - - // 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)); - - // TODO: last 3 bits in v_pos_norm should be a number between 0 and 5, rather than 0-2 and a direction. - // uint norm_axis = (f_pos_norm >> 30) & 0x3u; - // // Increase array access by 3 to access positive values - // uint norm_dir = ((f_pos_norm >> 29) & 0x1u) * 3u; - // Use an array to avoid conditional branching - vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u]; // Whether this face is facing fluid or not. bool faces_fluid = bool((f_pos_norm >> 28) & 0x1u); @@ -117,11 +179,14 @@ void main() { vec3 view_dir = -cam_to_frag; // vec3 view_dir = normalize(f_pos - cam_pos.xyz); - vec3 sun_dir = get_sun_dir(time_of_day.x); - vec3 moon_dir = get_moon_dir(time_of_day.x); + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ +#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) float f_alt = alt_at(f_pos.xy); - vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); +#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) + float f_alt = f_pos.z; +#endif float alpha = 1.0;//0.0001;//1.0; // TODO: Possibly angle with water surface into account? Since we can basically assume it's horizontal. @@ -145,15 +210,25 @@ void main() { float moon_shade_frac = horizon_at(f_pos, moon_dir); */ // 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, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); - float moon_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, moon_dir); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); +#endif + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, 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 = /*1.0;*/sun_shade_frac + moon_shade_frac; + // float shade_frac = /*1.0;*/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, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); float max_light = 0.0; @@ -171,21 +246,26 @@ void main() { // Computing light attenuation from water. vec3 emitted_light, reflected_light; // To account for prior saturation - float f_light = faces_fluid ? 1.0 : pow(f_light, 1.5); - float point_shadow = shadow_at(f_pos, f_norm); - max_light += get_sun_diffuse2(f_norm, /*time_of_day.x, */sun_dir, moon_dir, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, 1.0, emitted_light, reflected_light); + /*float */f_light = faces_fluid ? 1.0 : pow(f_light, 1.5); - emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW); - reflected_light *= f_light * point_shadow * shade_frac; - max_light *= f_light * point_shadow * shade_frac; + emitted_light = vec3(1.0); + reflected_light = vec3(1.0); + max_light += get_sun_diffuse2(/*time_of_day.x, */sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); - max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, 1.0, emitted_light, reflected_light); + // emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW); + // reflected_light *= f_light * point_shadow * shade_frac; + // max_light *= f_light * point_shadow * shade_frac; + emitted_light *= f_light; + reflected_light *= f_light; + max_light *= f_light; + + max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light); // float f_ao = 1.0; - float ao = /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1; - emitted_light *= ao; - reflected_light *= ao; + // float ao = /*pow(f_ao, 0.5)*/f_ao * 0.9 + 0.1; + // emitted_light *= ao; + // reflected_light *= ao; /* vec3 point_light = light_at(f_pos, f_norm); emitted_light += point_light; reflected_light += point_light; */ @@ -208,14 +288,19 @@ void main() { // light_reflection_factorplight_reflection_factor // vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light); - vec3 col = srgb_to_linear(f_col + hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0)) * 0.02); // Small-scale noise + vec3 f_chunk_pos = f_pos - (model_offs - focus_off.xyz); + vec3 col = /*srgb_to_linear*/(f_col + hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0)) * 0.01/* - 0.01*/); // Small-scale noise + // vec3 col = /*srgb_to_linear*/(f_col + hash(vec4(floor(f_pos * 3.0 - f_norm * 0.5), 0)) * 0.01); // Small-scale noise vec3 surf_color = illuminate(max_light, view_dir, col * emitted_light, col * reflected_light); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); +#if (CLOUD_MODE == CLOUD_MODE_REGULAR) + float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); vec4 clouds; - vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 1.0, true, clouds); - // vec3 color = surf_color; + vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 1.0, false, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); +#elif (CLOUD_MODE == CLOUD_MODE_NONE) + vec3 color = surf_color; +#endif tgt_color = vec4(color, 1.0); } diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl index 771afeebe0..e3d9615350 100644 --- a/assets/voxygen/shaders/terrain-vert.glsl +++ b/assets/voxygen/shaders/terrain-vert.glsl @@ -16,52 +16,97 @@ #define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN +// #define HAS_SHADOW_MAPS + #include #include #include +#include + in uint v_pos_norm; -in uint v_col_light; +// in uint v_col_light; +in uint v_atlas_pos; layout (std140) uniform u_locals { vec3 model_offs; float load_time; + ivec4 atlas_offs; }; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + out vec3 f_pos; -out vec3 f_chunk_pos; +// #ifdef FLUID_MODE_SHINY flat out uint f_pos_norm; + +// #if (SHADOW_MODE == SHADOW_MODE_MAP) +// out vec4 sun_pos; +// #endif + +// #endif // out float f_alt; // out vec4 f_shadow; -out vec3 f_col; -out float f_light; -out float f_ao; +// out vec3 f_col; +// out vec3 f_chunk_pos; +// out float f_ao; +/*centroid */out vec2 f_uv_pos; +// out vec3 light_pos[2]; +// out float f_light; + +// uniform sampler2DRect t_col_light; const int EXTRA_NEG_Z = 32768; void main() { // over it (if this vertex to see if it intersects. - f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); - f_pos = f_chunk_pos + model_offs; + // f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + f_pos = f_chunk_pos + model_offs - focus_off.xyz; // f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); // vec3 light_col = vec3( - // hash(floor(vec4(f_pos.x, 0, 0, 0))), - // hash(floor(vec4(0, f_pos.y, 0, 1))), - // hash(floor(vec4(0, 0, f_pos.z, 2))) + // hash(floor(vec4(f_chunk_pos.x, 0, 0, 0))), + // hash(floor(vec4(0, f_chunk_pos.y, 0, 1))), + // hash(floor(vec4(0, 0, f_chunk_pos.z, 2))) // ); // f_col = light_col;// f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; // f_light = 1.0;//float(v_col_light & 0x3Fu) / 64.0; // f_ao = 1.0;//float((v_col_light >> 6u) & 3u) / 4.0; - f_col = f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; - f_light = float(v_col_light & 0x3Fu) / 64.0; - f_ao = float((v_col_light >> 6u) & 3u) / 4.0; + // f_col = f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // f_light = float(v_col_light & 0x3Fu) / 64.0; + // f_ao = float((v_col_light >> 6u) & 3u) / 4.0; + // for (uint i = 0u; i < 1u/*light_shadow_count.z*/; ++i) { + // light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + // } + // vec2 texSize = textureSize(t_col_light, 0); + f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)); + +// #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)/*)*/; +// // } +// 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 + +// #ifdef FLUID_MODE_SHINY f_pos_norm = v_pos_norm; +// #endif // Also precalculate shadow texture and estimated terrain altitude. // f_alt = alt_at(f_pos.xy); @@ -104,11 +149,23 @@ void main() { // // wPoint -= wRayDir3 * wRayLength * n2 / n1; // vec3 newRay = dot(wRayDir, wRayNormal) < 0.0 && wIntersectsSurface ? wPoint - wRayDir3 * wRayLength * n2 / n1 : f_pos;// - (wRayfinal - wPoint) * n2 / n1; // wPoint + n2 * (wRayfinal - wPoint) - n2 / n1 * wRayLength * wRayDir3; +#ifdef HAS_SHADOW_MAPS gl_Position = - all_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 + gl_Position = all_mat * vec4(f_pos/*newRay*/, 1); +#endif + // gl_Position.y /= gl_Position.w; + // gl_Position.w = 1.0; + // gl_Position.z = -gl_Position.z; // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z *gl_Position.w; + // gl_Position.z = gl_Position.z / 100.0; + // gl_Position.z = gl_Position.z / 10000.0; // 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); + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/ui-vert.glsl b/assets/voxygen/shaders/ui-vert.glsl index 57d7230d6b..b60660ec3d 100644 --- a/assets/voxygen/shaders/ui-vert.glsl +++ b/assets/voxygen/shaders/ui-vert.glsl @@ -22,17 +22,21 @@ out vec4 f_color; void main() { f_color = v_color; + // vec2 v_pos = vec2(-1.0,1.0) * v_pos; + /* f_uv = vec2(1.0,1.0) * v_uv; */ + // vec2 v_uv = vec2(1.0,-1.0) * v_uv; + if (w_pos.w == 1.0) { f_uv = v_uv; - // Fixed scale In-game element - vec4 projected_pos = proj_mat * view_mat * vec4(w_pos.xyz, 1.0); - gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos, 0.0, 1.0); + // 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*/, -1.0, /*projected_pos.w*/1.0); } else if (v_mode == uint(3)) { // HACK: North facing source rectangle. vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); mat2 look_at = mat2(look_at_dir.y, look_at_dir.x, -look_at_dir.x, look_at_dir.y); f_uv = v_center + look_at * (v_uv - v_center); - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } else if (v_mode == uint(5)) { // HACK: North facing target rectangle. f_uv = v_uv; @@ -41,11 +45,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_len = v_pos - v_center; vec2 v_proj = look_at * vec2(v_len.x, v_len.y / aspect_ratio); - gl_Position = vec4(v_center + vec2(v_proj.x, v_proj.y * aspect_ratio), 0.0, 1.0); + gl_Position = vec4(v_center + vec2(v_proj.x, v_proj.y * aspect_ratio), -1.0, 1.0); } else { // Interface element f_uv = v_uv; - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } f_mode = v_mode; } diff --git a/client/Cargo.toml b/client/Cargo.toml index 260af68232..3eb5eb50c9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -16,6 +16,6 @@ num_cpus = "1.10.1" log = "0.4.8" rayon = "^1.3.0" specs = "0.15.1" -vek = { version = "0.10.0", features = ["serde"] } +vek = { version = "0.11.2", features = ["serde"] } hashbrown = { version = "0.6.2", features = ["rayon", "serde", "nightly"] } authc = { git = "https://gitlab.com/veloren/auth.git", rev = "65571ade0d954a0e0bd995fdb314854ff146ab97" } diff --git a/common/Cargo.toml b/common/Cargo.toml index 3a50a4e9f4..cfb99f6df6 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -11,7 +11,7 @@ no-assets = [] specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" } specs = { version = "0.15.1", features = ["serde", "nightly", "storage-event-control"] } -vek = { version = "0.10.0", features = ["serde"] } +vek = { version = "0.11.2", features = ["serde"] } dot_vox = "4.0.0" fxhash = "0.2.1" image = "0.22.3" diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index a69cc9b961..a0ea946de0 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -19,6 +19,79 @@ use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; use std::{fs::File, io::BufReader}; +/* pub trait PerBody { + type Humanoid; + type QuadrupedSmall; + type QuadrupedMedium; + type BirdMedium; + type FishMedium; + type Dragon; + type BirdSmall; + type FishSmall; + type BipedLarge; + type Object; + type Golem; + type Critter; +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[repr(u32)] +pub enum Body + where Meta: PerBody, +{ + Humanoid(Meta::Humanoid) = 0, + QuadrupedSmall(Meta::QuadrupedSmall) = 1, + QuadrupedMedium(Meta::QuadrupedMedium) = 2, + BirdMedium(Meta::BirdMedium) = 3, + FishMedium(Meta::FishMedium) = 4, + Dragon(Meta::Dragon) = 5, + BirdSmall(Meta::BirdSmall) = 6, + FishSmall(Meta::FishSmall) = 7, + BipedLarge(Meta::BipedLarge) = 8, + Object(Meta::Object) = 9, + Golem(Meta::Golem) = 10, + Critter(Meta::Critter) = 11, +} + +/// Metadata intended to be stored per-body, together with data intended to be +/// stored for each species for each body. +/// +/// NOTE: Deliberately don't (yet?) implement serialize. +#[derive(Clone, Debug, Deserialize)] +pub struct AllBodies { + pub humanoid: BodyData>, + pub quadruped_small: BodyData>, + pub quadruped_medium: BodyData>, + pub bird_medium: BodyData>, + pub fish_medium: BodyData, + pub dragon: BodyData>, + pub bird_small: BodyData, + pub fish_small: BodyData + pub biped_large: BodyData>, + pub object: BodyData, + pub golem: BodyData>, + pub critter: BodyData>, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct BodyType; + +impl PerBody for BodyType { + type Humanoid = humanoid::Body; + type QuadrupedSmall = quadruped_small::Body; + type QuadrupedMedium = quadruped_medium::Body; + type BirdMedium = bird_medium::Body; + type FishMedium = fish_medium::Body; + type Dragon = dragon::Body; + type BirdSmall = bird_small::Body; + type FishSmall = fish_small::Body; + type BipedLarge = biped_large::Body; + type Object = object::Body; + type Golem = golem::Body; + type Critter = critter::Body; +} +*/ + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u32)] pub enum Body { @@ -57,10 +130,14 @@ pub struct AllBodies { pub quadruped_small: BodyData>, pub quadruped_medium: BodyData>, pub bird_medium: BodyData>, + pub fish_medium: BodyData, + pub dragon: BodyData>, + pub bird_small: BodyData, + pub fish_small: BodyData, pub biped_large: BodyData>, + pub object: BodyData, pub golem: BodyData>, pub critter: BodyData>, - pub dragon: BodyData>, } /// Can only retrieve body metadata by direct index. @@ -82,6 +159,29 @@ impl core::ops::Index for AllBodies core::ops::Index<&'a Body> for AllBodies { + type Output = BodyMeta; + + #[inline] + fn index(&self, index: &Body) -> &Self::Output { + match index { + Body::Humanoid(_) => &self.humanoid.body, + Body::QuadrupedSmall(_) => &self.quadruped_small.body, + Body::QuadrupedMedium(_) => &self.quadruped_medium.body, + Body::BirdMedium(_) => &self.bird_medium.body, + Body::FishMedium(_) => &self.fish_medium.body, + Body::Dragon(_) => &self.dragon.body, + Body::BirdSmall(_) => &self.bird_small.body, + Body::FishSmall(_) => &self.fish_small.body, + Body::BipedLarge(_) => &self.biped_large.body, + Body::Object(_) => &self.object.body, + Body::Golem(_) => &self.golem.body, + Body::Critter(_) => &self.critter.body, + } + } +} + impl< BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>, SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>, diff --git a/common/src/vol.rs b/common/src/vol.rs index 85626c37d7..6d50048676 100644 --- a/common/src/vol.rs +++ b/common/src/vol.rs @@ -25,7 +25,7 @@ pub trait BaseVol { type Vox: Vox; type Error: Debug; - fn scaled_by(&self, scale: Vec3) -> Scaled + fn scaled_by(self, scale: Vec3) -> Scaled where Self: Sized, { diff --git a/common/src/volumes/scaled.rs b/common/src/volumes/scaled.rs index 44caa0bf51..c435c04096 100644 --- a/common/src/volumes/scaled.rs +++ b/common/src/volumes/scaled.rs @@ -1,28 +1,36 @@ use crate::vol::{BaseVol, ReadVol, SizedVol, Vox}; use vek::*; -pub struct Scaled<'a, V> { - pub inner: &'a V, +pub struct Scaled { + pub inner: V, pub scale: Vec3, } -impl<'a, V: BaseVol> BaseVol for Scaled<'a, V> { +impl BaseVol for Scaled { type Error = V::Error; type Vox = V::Vox; } -impl<'a, V: ReadVol> ReadVol for Scaled<'a, V> { +impl ReadVol for Scaled { #[inline(always)] fn get(&self, pos: Vec3) -> Result<&Self::Vox, Self::Error> { - let ideal_pos = pos.map2(self.scale, |e, scale| e as f32 / scale); - let pos = ideal_pos.map(|e| e.trunc() as i32); + // let ideal_pos = pos.map2(self.scale, |e, scale| (e as f32 + 0.5) / scale); + // let pos = ideal_pos.map(|e| e.trunc() as i32); + let min_pos = pos.map2(self.scale, |e, scale| ((e as f32) / scale).floor() as i32); + let max_pos = pos.map2(self.scale, |e, scale| { + ((e as f32 + 1.0) / scale).ceil() as i32 + }); + let pos = pos.map2(self.scale, |e, scale| { + (((e as f32 + 0.5) / scale) - 0.5).round() as i32 + }); - let ideal_search_size = Vec3::::one() / self.scale; + // let ideal_search_size = Vec3::::one() / self.scale; let range_iter = |i: usize| { std::iter::successors(Some(0), |p| Some(if *p < 0 { -*p } else { -(*p + 1) })) .take_while(move |p| { - ((ideal_pos[i] - ideal_search_size[i] / 2.0).round() as i32 - ..(ideal_pos[i] + ideal_search_size[i] / 2.0).round() as i32) + (min_pos[i]..max_pos[i]) + /* ((ideal_pos[i] - ideal_search_size[i] / 2.0).ceil() as i32 + ..(ideal_pos[i] + ideal_search_size[i] / 2.0).ceil() as i32) */ .contains(&(pos[i] + *p)) }) }; @@ -36,7 +44,7 @@ impl<'a, V: ReadVol> ReadVol for Scaled<'a, V> { } } -impl<'a, V: SizedVol> SizedVol for Scaled<'a, V> { +impl SizedVol for Scaled { #[inline(always)] fn lower_bound(&self) -> Vec3 { self.inner @@ -48,6 +56,6 @@ impl<'a, V: SizedVol> SizedVol for Scaled<'a, V> { fn upper_bound(&self) -> Vec3 { self.inner .upper_bound() - .map2(self.scale, |e, scale| (e as f32 * scale).ceil() as i32 + 1) + .map2(self.scale, |e, scale| (e as f32 * scale).ceil() as i32) } } diff --git a/server/Cargo.toml b/server/Cargo.toml index 2530a6d5d3..7b7e650eac 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,7 +16,7 @@ specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" } log = "0.4.8" specs = { version = "0.15.1", features = ["shred-derive"] } -vek = "0.10.0" +vek = { version = "0.11.2", features = ["serde"] } uvth = "3.1.1" lazy_static = "1.4.0" scan_fmt = "0.2.4" diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index cc4d84312f..b8f8e04227 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -9,7 +9,7 @@ default-run = "veloren-voxygen" # autobins = false [features] -gl = ["gfx_device_gl"] +gl = ["gfx_device_gl", "gfx_gl"] singleplayer = ["server"] tweak = ["const-tweaker"] @@ -22,6 +22,7 @@ client = { package = "veloren-client", path = "../client" } # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } +gfx_gl = { version = "0.6.1", optional = true } gfx_window_glutin = "0.31.0" glutin = "0.21.1" winit = { version = "0.19.4", features = ["serde"] } @@ -34,7 +35,7 @@ specs = "0.15.1" specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" } # Mathematics -vek = { version = "0.10.0", features = ["serde"] } +vek = { version = "0.11.2", features = ["serde"] } # Controller gilrs = { version = "0.7", features = ["serde"] } @@ -51,7 +52,7 @@ image = "0.22.3" serde = "1.0.102" serde_derive = "1.0.102" ron = "0.5.1" -guillotiere = { git = "https://github.com/Imberflur/guillotiere" } +guillotiere = "0.5.2" fern = { version = "0.5.8", features = ["colored"] } msgbox = { git = "https://github.com/bekker/msgbox-rs.git", rev = "68fe39a", optional = true } directories = "2.0.2" diff --git a/voxygen/benches/meshing_benchmark.rs b/voxygen/benches/meshing_benchmark.rs index 721d080512..f67d34949c 100644 --- a/voxygen/benches/meshing_benchmark.rs +++ b/voxygen/benches/meshing_benchmark.rs @@ -131,7 +131,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { c.bench( "meshing", Benchmark::new(&format!("Terrain mesh {}, {}", x, y), move |b| { - b.iter(|| volume.generate_mesh(black_box(range))) + b.iter(|| volume.generate_mesh(black_box((range, Vec2::new(8192, 8192))))) }) // Lower sample size to save time .sample_size(15), diff --git a/voxygen/examples/character_renderer.rs b/voxygen/examples/character_renderer.rs index c6b6849404..5721d1886b 100644 --- a/voxygen/examples/character_renderer.rs +++ b/voxygen/examples/character_renderer.rs @@ -14,17 +14,8 @@ fn main() { let (_context, device, factory, color_view, depth_view) = init_headless(context, dim); - let mut renderer = render::Renderer::new( - device, - factory, - color_view, - depth_view, - render::AaMode::SsaaX4, - render::CloudMode::Regular, - render::FluidMode::Shiny, - render::LightingMode::Ashikmin, - ) - .unwrap(); + let mut renderer = + render::Renderer::new(device, factory, color_view, depth_view, Default::default()).unwrap(); // Create character let body = comp::humanoid::Body::random(); @@ -62,7 +53,7 @@ fn main() { scene .camera_mut() .update(0.0, 1.0 / 60.0, scene_data.mouse_smoothing); - scene.maintain(&mut renderer, scene_data); + scene.maintain(&mut renderer, scene_data, None); // Render renderer.clear(); diff --git a/voxygen/src/anim/biped_large/mod.rs b/voxygen/src/anim/biped_large/mod.rs index e5b3c38776..77f83ab8c5 100644 --- a/voxygen/src/anim/biped_large/mod.rs +++ b/voxygen/src/anim/biped_large/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone, Default)] pub struct BipedLargeSkeleton { @@ -35,7 +35,10 @@ impl Skeleton for BipedLargeSkeleton { fn bone_count(&self) -> usize { 11 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let upper_torso_mat = self.upper_torso.compute_base_matrix(); let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); let shoulder_r_mat = self.shoulder_r.compute_base_matrix(); @@ -44,23 +47,17 @@ impl Skeleton for BipedLargeSkeleton { let torso_mat = self.torso.compute_base_matrix(); ( [ - FigureBoneData::new(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new(torso_mat * upper_torso_mat), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.lower_torso.compute_base_matrix(), - ), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_r_mat), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.hand_l.compute_base_matrix(), - ), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.hand_r.compute_base_matrix(), - ), - FigureBoneData::new(torso_mat * upper_torso_mat * leg_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * leg_r_mat), - FigureBoneData::new(self.foot_l.compute_base_matrix()), - FigureBoneData::new(self.foot_r.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat), + make_bone(torso_mat * upper_torso_mat * self.lower_torso.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat * shoulder_l_mat), + make_bone(torso_mat * upper_torso_mat * shoulder_r_mat), + make_bone(torso_mat * upper_torso_mat * self.hand_l.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat * self.hand_r.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat * leg_l_mat), + make_bone(torso_mat * upper_torso_mat * leg_r_mat), + make_bone(self.foot_l.compute_base_matrix()), + make_bone(self.foot_r.compute_base_matrix()), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/bird_medium/mod.rs b/voxygen/src/anim/bird_medium/mod.rs index 9d561c477a..ee7c63df92 100644 --- a/voxygen/src/anim/bird_medium/mod.rs +++ b/voxygen/src/anim/bird_medium/mod.rs @@ -8,7 +8,7 @@ pub use self::{fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone, Default)] pub struct BirdMediumSkeleton { @@ -30,18 +30,21 @@ impl Skeleton for BirdMediumSkeleton { fn bone_count(&self) -> usize { 7 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let torso_mat = self.torso.compute_base_matrix(); ( [ - FigureBoneData::new(torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new(torso_mat), - FigureBoneData::new(torso_mat * self.tail.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.wing_l.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.wing_r.compute_base_matrix()), - FigureBoneData::new(self.leg_l.compute_base_matrix()), - FigureBoneData::new(self.leg_r.compute_base_matrix()), + make_bone(torso_mat * self.head.compute_base_matrix()), + make_bone(torso_mat), + make_bone(torso_mat * self.tail.compute_base_matrix()), + make_bone(torso_mat * self.wing_l.compute_base_matrix()), + make_bone(torso_mat * self.wing_r.compute_base_matrix()), + make_bone(self.leg_l.compute_base_matrix()), + make_bone(self.leg_r.compute_base_matrix()), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/bird_small/mod.rs b/voxygen/src/anim/bird_small/mod.rs index 2bbf379cb2..94a0d6a51a 100644 --- a/voxygen/src/anim/bird_small/mod.rs +++ b/voxygen/src/anim/bird_small/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone)] pub struct BirdSmallSkeleton { @@ -34,15 +34,18 @@ impl Skeleton for BirdSmallSkeleton { fn bone_count(&self) -> usize { 4 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let torso_mat = self.torso.compute_base_matrix(); ( [ - FigureBoneData::new(self.head.compute_base_matrix() * torso_mat), - FigureBoneData::new(torso_mat), - FigureBoneData::new(self.wing_l.compute_base_matrix() * torso_mat), - FigureBoneData::new(self.wing_r.compute_base_matrix() * torso_mat), + make_bone(self.head.compute_base_matrix() * torso_mat), + make_bone(torso_mat), + make_bone(self.wing_l.compute_base_matrix() * torso_mat), + make_bone(self.wing_r.compute_base_matrix() * torso_mat), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index c867a3d25e..0102fcdac0 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -31,7 +31,7 @@ pub use self::{ use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp; -use vek::{Vec3, Vec4}; +use vek::{Mat4, Vec3, Vec4}; #[derive(Clone, Default)] pub struct CharacterSkeleton { @@ -65,7 +65,10 @@ impl Skeleton for CharacterSkeleton { fn bone_count(&self) -> usize { 15 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let chest_mat = self.chest.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix(); let l_hand_mat = self.l_hand.compute_base_matrix(); @@ -83,27 +86,21 @@ impl Skeleton for CharacterSkeleton { ( [ - FigureBoneData::new(torso_mat * chest_mat * head_mat), - FigureBoneData::new(torso_mat * chest_mat), - FigureBoneData::new(torso_mat * chest_mat * self.belt.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.back.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * shorts_mat), - FigureBoneData::new( - torso_mat * chest_mat * control_mat * l_control_mat * l_hand_mat, - ), - FigureBoneData::new( - torso_mat * chest_mat * control_mat * r_control_mat * r_hand_mat, - ), - FigureBoneData::new(torso_mat * self.l_foot.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.r_foot.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.l_shoulder.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * self.r_shoulder.compute_base_matrix()), - FigureBoneData::new(torso_mat * self.glider.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * control_mat * l_control_mat * main_mat), - FigureBoneData::new( - torso_mat * chest_mat * control_mat * r_control_mat * second_mat, - ), - FigureBoneData::new(lantern_final_mat), + make_bone(torso_mat * chest_mat * head_mat), + make_bone(torso_mat * chest_mat), + make_bone(torso_mat * chest_mat * self.belt.compute_base_matrix()), + make_bone(torso_mat * chest_mat * self.back.compute_base_matrix()), + make_bone(torso_mat * chest_mat * shorts_mat), + make_bone(torso_mat * chest_mat * control_mat * l_control_mat * l_hand_mat), + make_bone(torso_mat * chest_mat * control_mat * r_control_mat * r_hand_mat), + make_bone(torso_mat * self.l_foot.compute_base_matrix()), + make_bone(torso_mat * self.r_foot.compute_base_matrix()), + make_bone(torso_mat * chest_mat * self.l_shoulder.compute_base_matrix()), + make_bone(torso_mat * chest_mat * self.r_shoulder.compute_base_matrix()), + make_bone(torso_mat * self.glider.compute_base_matrix()), + make_bone(torso_mat * chest_mat * control_mat * l_control_mat * main_mat), + make_bone(torso_mat * chest_mat * control_mat * r_control_mat * second_mat), + make_bone(lantern_final_mat), FigureBoneData::default(), ], (lantern_final_mat * Vec4::new(0.0, 0.0, 0.0, 1.0)).xyz(), diff --git a/voxygen/src/anim/critter/mod.rs b/voxygen/src/anim/critter/mod.rs index 56d40c2cd2..00bb7a60c0 100644 --- a/voxygen/src/anim/critter/mod.rs +++ b/voxygen/src/anim/critter/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone, Default)] pub struct CritterSkeleton { @@ -35,14 +35,17 @@ impl Skeleton for CritterSkeleton { fn bone_count(&self) -> usize { 5 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { ( [ - FigureBoneData::new(self.head.compute_base_matrix()), - FigureBoneData::new(self.chest.compute_base_matrix()), - FigureBoneData::new(self.feet_f.compute_base_matrix()), - FigureBoneData::new(self.feet_b.compute_base_matrix()), - FigureBoneData::new(self.tail.compute_base_matrix()), + make_bone(self.head.compute_base_matrix()), + make_bone(self.chest.compute_base_matrix()), + make_bone(self.feet_f.compute_base_matrix()), + make_bone(self.feet_b.compute_base_matrix()), + make_bone(self.tail.compute_base_matrix()), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/dragon/mod.rs b/voxygen/src/anim/dragon/mod.rs index 8e4ea6ff40..816badf83d 100644 --- a/voxygen/src/anim/dragon/mod.rs +++ b/voxygen/src/anim/dragon/mod.rs @@ -8,7 +8,7 @@ pub use self::{fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone, Default)] pub struct DragonSkeleton { @@ -38,7 +38,10 @@ impl Skeleton for DragonSkeleton { fn bone_count(&self) -> usize { 15 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let head_upper_mat = self.head_upper.compute_base_matrix(); let head_lower_mat = self.head_lower.compute_base_matrix(); let chest_front_mat = self.chest_front.compute_base_matrix(); @@ -48,35 +51,31 @@ impl Skeleton for DragonSkeleton { let tail_front_mat = self.tail_front.compute_base_matrix(); ( [ - FigureBoneData::new(chest_front_mat * head_lower_mat * head_upper_mat), - FigureBoneData::new(chest_front_mat * head_lower_mat), - FigureBoneData::new( + make_bone(chest_front_mat * head_lower_mat * head_upper_mat), + make_bone(chest_front_mat * head_lower_mat), + make_bone( chest_front_mat * head_lower_mat * head_upper_mat * self.jaw.compute_base_matrix(), ), - FigureBoneData::new(chest_front_mat), - FigureBoneData::new(chest_front_mat * self.chest_rear.compute_base_matrix()), - FigureBoneData::new(chest_front_mat * chest_rear_mat * tail_front_mat), - FigureBoneData::new( + make_bone(chest_front_mat), + make_bone(chest_front_mat * self.chest_rear.compute_base_matrix()), + make_bone(chest_front_mat * chest_rear_mat * tail_front_mat), + make_bone( chest_front_mat * chest_rear_mat * tail_front_mat * self.tail_rear.compute_base_matrix(), ), - FigureBoneData::new(chest_front_mat * self.wing_in_l.compute_base_matrix()), - FigureBoneData::new(chest_front_mat * self.wing_in_r.compute_base_matrix()), - FigureBoneData::new( - chest_front_mat * wing_in_l_mat * self.wing_out_l.compute_base_matrix(), - ), - FigureBoneData::new( - chest_front_mat * wing_in_r_mat * self.wing_out_r.compute_base_matrix(), - ), - FigureBoneData::new(self.foot_fl.compute_base_matrix()), - FigureBoneData::new(self.foot_fr.compute_base_matrix()), - FigureBoneData::new(self.foot_bl.compute_base_matrix()), - FigureBoneData::new(self.foot_br.compute_base_matrix()), + make_bone(chest_front_mat * self.wing_in_l.compute_base_matrix()), + make_bone(chest_front_mat * self.wing_in_r.compute_base_matrix()), + make_bone(chest_front_mat * wing_in_l_mat * self.wing_out_l.compute_base_matrix()), + make_bone(chest_front_mat * wing_in_r_mat * self.wing_out_r.compute_base_matrix()), + make_bone(self.foot_fl.compute_base_matrix()), + make_bone(self.foot_fr.compute_base_matrix()), + make_bone(self.foot_bl.compute_base_matrix()), + make_bone(self.foot_br.compute_base_matrix()), FigureBoneData::default(), ], Vec3::default(), diff --git a/voxygen/src/anim/fish_medium/mod.rs b/voxygen/src/anim/fish_medium/mod.rs index 8853f40f57..6bd3bba0d0 100644 --- a/voxygen/src/anim/fish_medium/mod.rs +++ b/voxygen/src/anim/fish_medium/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone)] pub struct FishMediumSkeleton { @@ -38,18 +38,21 @@ impl Skeleton for FishMediumSkeleton { fn bone_count(&self) -> usize { 6 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let torso_mat = self.torso.compute_base_matrix(); let rear_mat = self.rear.compute_base_matrix(); ( [ - FigureBoneData::new(self.head.compute_base_matrix() * torso_mat), - FigureBoneData::new(torso_mat), - FigureBoneData::new(rear_mat * torso_mat), - FigureBoneData::new(self.tail.compute_base_matrix() * rear_mat), - FigureBoneData::new(self.fin_l.compute_base_matrix() * rear_mat), - FigureBoneData::new(self.fin_r.compute_base_matrix() * rear_mat), + make_bone(self.head.compute_base_matrix() * torso_mat), + make_bone(torso_mat), + make_bone(rear_mat * torso_mat), + make_bone(self.tail.compute_base_matrix() * rear_mat), + make_bone(self.fin_l.compute_base_matrix() * rear_mat), + make_bone(self.fin_r.compute_base_matrix() * rear_mat), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/fish_small/mod.rs b/voxygen/src/anim/fish_small/mod.rs index 0912647f5c..2aa8332629 100644 --- a/voxygen/src/anim/fish_small/mod.rs +++ b/voxygen/src/anim/fish_small/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone)] pub struct FishSmallSkeleton { @@ -30,13 +30,16 @@ impl Skeleton for FishSmallSkeleton { fn bone_count(&self) -> usize { 2 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let torso_mat = self.torso.compute_base_matrix(); ( [ - FigureBoneData::new(torso_mat), - FigureBoneData::new(self.tail.compute_base_matrix() * torso_mat), + make_bone(torso_mat), + make_bone(self.tail.compute_base_matrix() * torso_mat), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/fixture/mod.rs b/voxygen/src/anim/fixture/mod.rs index ae34c70e01..b325a3070d 100644 --- a/voxygen/src/anim/fixture/mod.rs +++ b/voxygen/src/anim/fixture/mod.rs @@ -1,6 +1,6 @@ use super::Skeleton; use crate::render::FigureBoneData; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone)] pub struct FixtureSkeleton; @@ -16,25 +16,28 @@ impl Skeleton for FixtureSkeleton { fn bone_count(&self) -> usize { 1 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + _make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { ( [ - FigureBoneData::new(vek::Mat4::identity()), // <-- This is actually a bone! - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), + FigureBoneData::default(), // <-- This is actually a bone! + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ], Vec3::default(), ) diff --git a/voxygen/src/anim/golem/mod.rs b/voxygen/src/anim/golem/mod.rs index fceda8c230..89f38990eb 100644 --- a/voxygen/src/anim/golem/mod.rs +++ b/voxygen/src/anim/golem/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone)] pub struct GolemSkeleton { @@ -46,7 +46,10 @@ impl GolemSkeleton { impl Skeleton for GolemSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let upper_torso_mat = self.upper_torso.compute_base_matrix(); let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); let shoulder_r_mat = self.shoulder_r.compute_base_matrix(); @@ -57,20 +60,16 @@ impl Skeleton for GolemSkeleton { let foot_r_mat = self.foot_r.compute_base_matrix(); ( [ - FigureBoneData::new(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new(torso_mat * upper_torso_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * shoulder_r_mat), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.hand_l.compute_base_matrix(), - ), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.hand_r.compute_base_matrix(), - ), - FigureBoneData::new(foot_l_mat * leg_l_mat), - FigureBoneData::new(foot_r_mat * leg_r_mat), - FigureBoneData::new(foot_l_mat), - FigureBoneData::new(foot_r_mat), + make_bone(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat), + make_bone(torso_mat * upper_torso_mat * shoulder_l_mat), + make_bone(torso_mat * upper_torso_mat * shoulder_r_mat), + make_bone(torso_mat * upper_torso_mat * self.hand_l.compute_base_matrix()), + make_bone(torso_mat * upper_torso_mat * self.hand_r.compute_base_matrix()), + make_bone(foot_l_mat * leg_l_mat), + make_bone(foot_r_mat * leg_r_mat), + make_bone(foot_l_mat), + make_bone(foot_r_mat), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index 3682db6374..be64a7610e 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -54,7 +54,10 @@ pub trait Skeleton: Send + Sync + 'static { fn bone_count(&self) -> usize { 16 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3); + fn compute_matrices) -> FigureBoneData>( + &self, + make_bone: F, + ) -> ([FigureBoneData; 16], Vec3); /// Change the current skeleton to be more like `target`. fn interpolate(&mut self, target: &Self, dt: f32); diff --git a/voxygen/src/anim/object/mod.rs b/voxygen/src/anim/object/mod.rs index 71dff4052d..c2056e2c79 100644 --- a/voxygen/src/anim/object/mod.rs +++ b/voxygen/src/anim/object/mod.rs @@ -17,25 +17,28 @@ impl Skeleton for ObjectSkeleton { fn bone_count(&self) -> usize { 1 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { ( [ - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), - FigureBoneData::new(vek::Mat4::identity()), + make_bone(Mat4::scaling_3d(Vec3::broadcast(SCALE))), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ], Vec3::default(), ) diff --git a/voxygen/src/anim/quadruped_medium/mod.rs b/voxygen/src/anim/quadruped_medium/mod.rs index e69cfe6fb1..7aea25535c 100644 --- a/voxygen/src/anim/quadruped_medium/mod.rs +++ b/voxygen/src/anim/quadruped_medium/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone, Default)] pub struct QuadrupedMediumSkeleton { @@ -34,24 +34,27 @@ impl Skeleton for QuadrupedMediumSkeleton { fn bone_count(&self) -> usize { 11 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { let ears_mat = self.ears.compute_base_matrix(); let head_upper_mat = self.head_upper.compute_base_matrix(); let head_lower_mat = self.head_lower.compute_base_matrix(); let torso_mid_mat = self.torso_mid.compute_base_matrix(); ( [ - FigureBoneData::new(head_upper_mat), - FigureBoneData::new(head_upper_mat * head_lower_mat), - FigureBoneData::new(head_upper_mat * self.jaw.compute_base_matrix()), - FigureBoneData::new(torso_mid_mat * self.tail.compute_base_matrix()), - FigureBoneData::new(self.torso_back.compute_base_matrix()), - FigureBoneData::new(torso_mid_mat), - FigureBoneData::new(head_upper_mat * ears_mat), - FigureBoneData::new(self.foot_lf.compute_base_matrix()), - FigureBoneData::new(self.foot_rf.compute_base_matrix()), - FigureBoneData::new(self.foot_lb.compute_base_matrix()), - FigureBoneData::new(self.foot_rb.compute_base_matrix()), + make_bone(head_upper_mat), + make_bone(head_upper_mat * head_lower_mat), + make_bone(head_upper_mat * self.jaw.compute_base_matrix()), + make_bone(torso_mid_mat * self.tail.compute_base_matrix()), + make_bone(self.torso_back.compute_base_matrix()), + make_bone(torso_mid_mat), + make_bone(head_upper_mat * ears_mat), + make_bone(self.foot_lf.compute_base_matrix()), + make_bone(self.foot_rf.compute_base_matrix()), + make_bone(self.foot_lb.compute_base_matrix()), + make_bone(self.foot_rb.compute_base_matrix()), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/anim/quadruped_small/mod.rs b/voxygen/src/anim/quadruped_small/mod.rs index 7b2770c077..633f47f333 100644 --- a/voxygen/src/anim/quadruped_small/mod.rs +++ b/voxygen/src/anim/quadruped_small/mod.rs @@ -8,7 +8,7 @@ pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; use super::{Bone, Skeleton}; use crate::render::FigureBoneData; use common::comp::{self}; -use vek::Vec3; +use vek::{Mat4, Vec3}; #[derive(Clone, Default)] pub struct QuadrupedSmallSkeleton { @@ -29,15 +29,18 @@ impl Skeleton for QuadrupedSmallSkeleton { fn bone_count(&self) -> usize { 6 } - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { + fn compute_matrices) -> FigureBoneData>( + &self, + mut make_bone: F, + ) -> ([FigureBoneData; 16], Vec3) { ( [ - FigureBoneData::new(self.head.compute_base_matrix()), - FigureBoneData::new(self.chest.compute_base_matrix()), - FigureBoneData::new(self.leg_lf.compute_base_matrix()), - FigureBoneData::new(self.leg_rf.compute_base_matrix()), - FigureBoneData::new(self.leg_lb.compute_base_matrix()), - FigureBoneData::new(self.leg_rb.compute_base_matrix()), + make_bone(self.head.compute_base_matrix()), + make_bone(self.chest.compute_base_matrix()), + make_bone(self.leg_lf.compute_base_matrix()), + make_bone(self.leg_rf.compute_base_matrix()), + make_bone(self.leg_lb.compute_base_matrix()), + make_bone(self.leg_rb.compute_base_matrix()), FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a24904fec9..f25d77f9bc 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -37,7 +37,7 @@ use spell::Spell; use crate::{ ecs::comp as vcomp, i18n::{i18n_asset_key, LanguageMetadata, VoxygenLocalization}, - render::{AaMode, CloudMode, Consts, FluidMode, Globals, LightingMode, Renderer}, + render::{Consts, Globals, RenderMode, Renderer}, scene::camera::{self, Camera}, ui::{fonts::ConrodVoxygenFonts, slot, Graphic, Ingameable, ScaleMode, Ui}, window::{Event as WinEvent, GameInput}, @@ -231,9 +231,6 @@ pub enum Event { ChangeGamma(f32), AdjustWindowSize([u16; 2]), ToggleFullscreen, - ChangeAaMode(AaMode), - ChangeCloudMode(CloudMode), - ChangeFluidMode(FluidMode), CrosshairTransp(f32), ChatTransp(f32), CrosshairType(CrosshairType), @@ -256,7 +253,7 @@ pub enum Event { ChangeLanguage(LanguageMetadata), ChangeBinding(GameInput), ChangeFreeLookBehavior(PressBehavior), - ChangeLightingMode(LightingMode), + ChangeRenderMode(RenderMode), } // TODO: Are these the possible layouts we want? @@ -1897,20 +1894,11 @@ impl Hud { settings_window::Event::AdjustGamma(new_gamma) => { events.push(Event::ChangeGamma(new_gamma)); }, - settings_window::Event::ChangeAaMode(new_aa_mode) => { - events.push(Event::ChangeAaMode(new_aa_mode)); - }, - settings_window::Event::ChangeCloudMode(new_cloud_mode) => { - events.push(Event::ChangeCloudMode(new_cloud_mode)); - }, - settings_window::Event::ChangeFluidMode(new_fluid_mode) => { - events.push(Event::ChangeFluidMode(new_fluid_mode)); - }, settings_window::Event::ChangeLanguage(language) => { events.push(Event::ChangeLanguage(language)); }, - settings_window::Event::ChangeLightingMode(new_lighting_mode) => { - events.push(Event::ChangeLightingMode(new_lighting_mode)); + settings_window::Event::ChangeRenderMode(new_render_mode) => { + events.push(Event::ChangeRenderMode(new_render_mode)); }, settings_window::Event::ToggleFullscreen => { events.push(Event::ToggleFullscreen); diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 8b7e6e1a91..5c4f0c3efe 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ i18n::{list_localizations, LanguageMetadata, VoxygenLocalization}, - render::{AaMode, CloudMode, FluidMode, LightingMode}, + render::{AaMode, CloudMode, FluidMode, LightingMode, RenderMode, ShadowMode}, ui::{fonts::ConrodVoxygenFonts, ImageSlider, ScaleMode, ToggleButton}, window::GameInput, GlobalState, @@ -117,6 +117,8 @@ widget_ids! { fullscreen_label, lighting_mode_text, lighting_mode_list, + shadow_mode_text, + shadow_mode_list, save_window_size_button, audio_volume_slider, audio_volume_text, @@ -227,10 +229,7 @@ pub enum Event { AdjustGamma(f32), AdjustWindowSize([u16; 2]), ToggleFullscreen, - ChangeAaMode(AaMode), - ChangeCloudMode(CloudMode), - ChangeFluidMode(FluidMode), - ChangeLightingMode(LightingMode), + ChangeRenderMode(RenderMode), AdjustMusicVolume(f32), AdjustSfxVolume(f32), ChangeAudioDevice(String), @@ -1774,7 +1773,7 @@ impl<'a> Widget for SettingsWindow<'a> { // Get which AA mode is currently active let selected = mode_list .iter() - .position(|x| *x == self.global_state.settings.graphics.aa_mode); + .position(|x| *x == self.global_state.settings.graphics.render_mode.aa); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1784,7 +1783,10 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.aa_mode_text, 8.0) .set(state.ids.aa_mode_list, ui) { - events.push(Event::ChangeAaMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(RenderMode { + aa: mode_list[clicked], + ..self.global_state.settings.graphics.render_mode + })); } // CloudMode @@ -1810,7 +1812,7 @@ impl<'a> Widget for SettingsWindow<'a> { // Get which cloud rendering mode is currently active let selected = mode_list .iter() - .position(|x| *x == self.global_state.settings.graphics.cloud_mode); + .position(|x| *x == self.global_state.settings.graphics.render_mode.cloud); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1820,7 +1822,10 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.cloud_mode_text, 8.0) .set(state.ids.cloud_mode_list, ui) { - events.push(Event::ChangeCloudMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(RenderMode { + cloud: mode_list[clicked], + ..self.global_state.settings.graphics.render_mode + })); } // FluidMode @@ -1848,7 +1853,7 @@ impl<'a> Widget for SettingsWindow<'a> { // Get which fluid rendering mode is currently active let selected = mode_list .iter() - .position(|x| *x == self.global_state.settings.graphics.fluid_mode); + .position(|x| *x == self.global_state.settings.graphics.render_mode.fluid); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1858,7 +1863,10 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.fluid_mode_text, 8.0) .set(state.ids.fluid_mode_list, ui) { - events.push(Event::ChangeFluidMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(RenderMode { + fluid: mode_list[clicked], + ..self.global_state.settings.graphics.render_mode + })); } // LightingMode @@ -1893,7 +1901,7 @@ impl<'a> Widget for SettingsWindow<'a> { // Get which lighting rendering mode is currently active let selected = mode_list .iter() - .position(|x| *x == self.global_state.settings.graphics.lighting_mode); + .position(|x| *x == self.global_state.settings.graphics.render_mode.lighting); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1903,14 +1911,61 @@ impl<'a> Widget for SettingsWindow<'a> { .down_from(state.ids.lighting_mode_text, 8.0) .set(state.ids.lighting_mode_list, ui) { - events.push(Event::ChangeLightingMode(mode_list[clicked])); + events.push(Event::ChangeRenderMode(RenderMode { + lighting: mode_list[clicked], + ..self.global_state.settings.graphics.render_mode + })); + } + + // ShadowMode + Text::new( + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode"), + ) + .down_from(state.ids.lighting_mode_list, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.shadow_mode_text, ui); + + let mode_list = [ShadowMode::None, ShadowMode::Cheap, ShadowMode::Map]; + let mode_label_list = [ + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.none"), + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.cheap"), + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.map"), + ]; + + // Get which shadow rendering mode is currently active + let selected = mode_list + .iter() + .position(|x| *x == self.global_state.settings.graphics.render_mode.shadow); + + if let Some(clicked) = DropDownList::new(&mode_label_list, selected) + .w_h(400.0, 22.0) + .color(MENU_BG) + .label_color(TEXT_COLOR) + .label_font_id(self.fonts.cyri.conrod_id) + .down_from(state.ids.shadow_mode_text, 8.0) + .set(state.ids.shadow_mode_list, ui) + { + events.push(Event::ChangeRenderMode(RenderMode { + shadow: mode_list[clicked], + ..self.global_state.settings.graphics.render_mode + })); } // Fullscreen Text::new(&self.localized_strings.get("hud.settings.fullscreen")) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .down_from(state.ids.lighting_mode_list, 8.0) + .down_from(state.ids.shadow_mode_list, 8.0) .color(TEXT_COLOR) .set(state.ids.fullscreen_label, ui); diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 1bd8b2bffb..ffabe79693 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -61,7 +61,9 @@ impl PlayState for CharSelectionState { } } - global_state.window.renderer_mut().clear(); + let renderer = global_state.window.renderer_mut(); + renderer.clear(); + renderer.clear_shadows(); // Maintain the UI. let events = self @@ -123,6 +125,7 @@ impl PlayState for CharSelectionState { }); // Maintain the scene. + let loadout = self.char_selection_ui.get_loadout(); { let client = self.client.borrow(); let scene_data = scene::SceneData { @@ -138,12 +141,14 @@ impl PlayState for CharSelectionState { .figure_lod_render_distance as f32, }; - self.scene - .maintain(global_state.window.renderer_mut(), scene_data); + self.scene.maintain( + global_state.window.renderer_mut(), + scene_data, + loadout.as_ref(), + ); } // Render the scene. - let loadout = self.char_selection_ui.get_loadout(); self.scene.render( global_state.window.renderer_mut(), self.client.borrow().get_tick(), diff --git a/voxygen/src/mesh/greedy.rs b/voxygen/src/mesh/greedy.rs new file mode 100644 index 0000000000..afc7326cd1 --- /dev/null +++ b/voxygen/src/mesh/greedy.rs @@ -0,0 +1,778 @@ +use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline}; +use vek::*; + +type TerrainVertex = ::Vertex; + +/// A suspended greedy mesh, with enough information to recover color data. +/// +/// The reason this exists is that greedy meshing is split into two parts. +/// First, the meshing itself needs to be performed; secondly, we generate a +/// texture atlas. We do things in this order to avoid having to copy all the +/// old vertices to the correct location. However, when trying to use the same +/// texture atlas for more than one model, this approach runs into the +/// problem that enough model information needs to be remembered to be able to +/// generate the colors after the function returns, so we box up the actual +/// coloring part as a continuation. When called with a final tile size and +/// vector, the continuation will consume the color data and write it to the +/// vector. +pub type SuspendedMesh<'a> = dyn for<'r> FnOnce(&'r mut ColLightInfo) + 'a; + +/// Shared state for a greedy mesh, potentially passed along to multiple models. +/// +/// For an explanation of why we want this, see `SuspendedMesh`. +pub struct GreedyMesh<'a> { + atlas: guillotiere::SimpleAtlasAllocator, + col_lights_size: Vec2, + max_size: guillotiere::Size, + suspended: Vec>>, +} + +impl<'a> GreedyMesh<'a> { + pub fn new(max_size: guillotiere::Size) -> Self { + let min_max_dim = max_size.width.min(max_size.height); + assert!( + min_max_dim >= 4, + "min_max_dim={:?} >= 4 ({:?}", + min_max_dim, + max_size + ); + // TODO: Collect information to see if we can choose a good value here. + let large_size_threshold = 256.min(min_max_dim / 2 + 1); + let small_size_threshold = 33.min(large_size_threshold / 2 + 1); + let size = guillotiere::Size::new(32, 32).min(max_size); + let atlas = + guillotiere::SimpleAtlasAllocator::with_options(size, &guillotiere::AllocatorOptions { + snap_size: 1, + small_size_threshold, + large_size_threshold, + }); + let col_lights_size = Vec2::new(1u16, 1u16); + Self { + atlas, + col_lights_size, + max_size, + suspended: Vec::new(), + } + } + + pub fn push( + &mut self, + data: D, + draw_delta: Vec3, + greedy_size: Vec3, + greedy_size_cross: Vec3, + get_light: impl for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, + get_color: impl for<'r> FnMut(&'r mut D, Vec3) -> Rgb + 'a, + get_opacity: impl for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, + should_draw: impl for<'r> FnMut( + &'r mut D, + Vec3, + Vec3, + Vec2>, + ) -> Option<(bool, M)>, + // create_shadow: impl for<'r> Fn(Vec3, Vec3, &'r M) -> S::Vertex, + // create_opaque: impl for<'r> Fn(Vec2, Vec3, Vec3, &'r M) -> O::Vertex, + push_quad: impl FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), + ) -> Aabb { + let (bounds, /* opaque, *//*shadow, */ cont) = greedy_mesh( + &mut self.atlas, + &mut self.col_lights_size, + self.max_size, + data, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + // create_shadow, + // create_opaque, + push_quad, + ); + self.suspended.push(cont); + bounds + } + + pub fn finalize(self) -> ColLightInfo { + let cur_size = self.col_lights_size; + let col_lights = vec![/*Default::default()*/TerrainVertex::make_col_light(254, Rgb::broadcast(254)); 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| { + cont(&mut col_lights_info); + }); + col_lights_info + } + + pub fn max_size(&self) -> guillotiere::Size { self.max_size } +} + +/// Perform greedy meshing on a model, separately producing "pure" model data +/// (the shadow mesh), raw light and color data ready to be used as a texture +/// (the returned vector of ColLights, together with their width and height), +/// and the atlas positions (the opaque mesh) used to connect the shadow +/// information with the light and color information. +/// +/// The opaque and shadow data are in the same order and it is intended that +/// they be used together as vertex buffers for display purposes. Thus, if you +/// perform further manipulation on the meshes after this function returns, such +/// as merges, please be sure that all those operations preserve this +/// relationship. +/// +/// TODO: Consider supporting shared texture atlases (this might come in handy +/// for mobs or sprites, for instance).a +/// +/// TODO: Add assertions to make this more robust. +/// +/// Parameter description: +/// +/// `max_size`: +/// The maximum allowable size of the texture atlas used to store the +/// light/color data for this mesh. +/// +/// NOTE: It is an error to pass any size > u16::MAX. +/// +/// Even aside from the above limitation, this will not necessarily always be +/// the same as the maximum atlas size supported by the hardware. For instance, +/// since we want to reserve 4 bits for a bone index for figures in their shadow +/// vertex, the atlas parameter for figures has to have at least 2 bits of the +/// normal; thus, it can only take up at most 30 bits total, meaning we are +/// restricted to "only" at most 2^15 × 2^15 atlases even if the hardware +/// supports larger ones. +/// +/// `draw_delta`: The minimum position to mesh, in the coordinate system used +/// for queries against the volume. +/// +/// `greedy_size`: +/// For each dimension i, for faces drawn in planes *parallel* to i, represents +/// the number of voxels considered along dimenson i in those planes, starting +/// from `draw_delta`. +/// +/// `greedy_size_cross`: +/// For each dimension i, represents the number of planes considered +/// *orthogonal* to dimension i, starting from `draw_delta`. This should +/// usually be the same as greedy_size. +/// +/// An important exception is during chunk rendering (where vertical faces at +/// chunk boundaries would otherwise be rendered twice, and also force us to use +/// more than 5 bits to represent x and y positions--though there may be a +/// clever way aruond the latter). Thus, for chunk rendering we set the number +/// of *vertical* planes to one less than the chunk size along the +/// x and y dimensions, but keep the number of *horizontal* planes large enough +/// to cover the whole chunk. +/// +/// `get_light`: +/// Given a position, return the lighting information for the voxel at that +/// position. +/// +/// `get_color`: +/// Given a position, return the color information for the voxel at that +/// position. +/// +/// `get_opacity`: +/// Given a position, return the opacity information for the voxel at that +/// position. Currently, we don't support real translucent lighting, so the +/// value should either be `false` (for opaque blocks) or `true` (otherwise). +/// +/// `should_draw`: +/// Given a position and a normal, should we draw the face between the position +/// and position - normal (i.e. the voxel "below" this vertex)? If so, provide +/// its orientation, together with any other meta information required for the +/// mesh that needs to split up faces. For example, terrain faces currently +/// record a bit indicating whether they are exposed to water or not, so we +/// should not merge faces where one is submerged in water and the other is not, +/// even if they otherwise have the same orientation, dimensions, and are +/// next to each other. +/// +/// `create_shadow`: +/// Create a shadow vertex (used for both shadow and display rendering) +/// given its position, normal, and meta information. Note that the position +/// received here is relative to `draw_delta`--it still needs to be translated +/// to mesh coordinates. +/// +/// `create_opaque`: +/// Create an opauqe vertex (used for only display rendering) from an atlas +/// position, normal, and meta information. +fn greedy_mesh< + 'a, /* , S: render::Pipeline*//*, O: render::Pipeline */ + M: PartialEq, + D: 'a, +>( + atlas: &mut guillotiere::SimpleAtlasAllocator, + col_lights_size: &mut Vec2, + max_size: guillotiere::Size, + mut data: D, + draw_delta: Vec3, + greedy_size: Vec3, + greedy_size_cross: Vec3, + get_light: impl for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, + get_color: impl for<'r> FnMut(&'r mut D, Vec3) -> Rgb + 'a, + get_opacity: impl for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, + mut should_draw: impl for<'r> FnMut( + &'r mut D, + Vec3, + Vec3, + Vec2>, + ) -> Option<(bool, M)>, + // create_shadow: impl Fn(Vec3, Vec3, &M) -> S::Vertex, + // create_opaque: impl Fn(Vec2, Vec3, Vec3, &M) -> O::Vertex, + mut push_quad: impl FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), +) -> ( + Aabb, + // Mesh, + // Mesh, + Box>, +) { + // let mut opaque_mesh = Mesh::new(); + // let mut shadow_mesh = Mesh::new(); + + // TODO: Collect information to see if we can choose a good value here. + let mut todo_rects = Vec::with_capacity(1024); + + /* let mut bounds = Aabb { + min: Vec3::zero(), + max: Vec3::zero(), + }; */ + + /* let compute_bounds = |pos: Vec3, dim: Vec2, uv: Vec2>/*, norm: Vec3, faces_forward: bool*/| { + Aabb { + min: pos, + max: pos + uv.x.map(usize::from) * dim.x + uv.y.map(usize::from) * dim.y, + } + }; */ + + // x (u = y, v = z) + greedy_mesh_cross_section( + Vec3::new(greedy_size.y, greedy_size.z, greedy_size_cross.x), + |pos| { + should_draw( + &mut data, + draw_delta + Vec3::new(pos.z, pos.x, pos.y), + Vec3::unit_x(), /* , pos.z, 0, x_size */ + Vec2::new(Vec3::unit_y(), Vec3::unit_z()), + ) + }, + |pos, dim, &(faces_forward, ref meta)| { + let pos = Vec3::new(pos.z, pos.x, pos.y); + let uv = Vec2::new(Vec3::unit_y(), Vec3::unit_z()); + let norm = Vec3::unit_x(); + // bounds.expand_to_contain(compute_bounds(pos, dim, uv)); + let atlas_pos = if let Some(atlas_pos) = add_to_atlas( + atlas, + &mut todo_rects, + pos, + uv, + dim, + norm, + faces_forward, + max_size, + col_lights_size, + ) { + // assert!(atlas_pos.max.x - atlas_pos.min.x == dim.x as i32); + // assert!(atlas_pos.max.y - atlas_pos.min.y == dim.y as i32); + atlas_pos + } else { + return; + }; + create_quad_greedy( + // &mut shadow_mesh, + // &mut opaque_mesh, + /* Vec3::new(pos.z, pos.x, pos.y) */ + pos, + dim, + uv, + norm, + faces_forward, + // Rgba::from_opaque(flat_get(pos).color), + // lightm + // ao, + meta, + atlas_pos, + // |pos| flat_get(pos), + // |pos, norm, meta| create_shadow(pos, norm, meta), + // |atlas_pos, pos, norm, meta| create_opaque(atlas_pos, pos, norm, meta), + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + // y (u = z, v = x) + greedy_mesh_cross_section( + Vec3::new(greedy_size.z, greedy_size.x, greedy_size_cross.y), + |pos| { + should_draw( + &mut data, + draw_delta + Vec3::new(pos.y, pos.z, pos.x), + Vec3::unit_y(), + Vec2::new(Vec3::unit_z(), Vec3::unit_x()), + ) + }, + |pos, dim, &(faces_forward, ref meta)| { + let pos = Vec3::new(pos.y, pos.z, pos.x); + let uv = Vec2::new(Vec3::unit_z(), Vec3::unit_x()); + let norm = Vec3::unit_y(); + // bounds.expand_to_contain(compute_bounds(pos, dim, uv)); + let atlas_pos = if let Some(atlas_pos) = add_to_atlas( + atlas, + &mut todo_rects, + pos, + uv, + dim, + norm, + faces_forward, + max_size, + col_lights_size, + ) { + atlas_pos + } else { + return; + }; + create_quad_greedy( + // &mut shadow_mesh, + // &mut opaque_mesh, + pos, + dim, + uv, + norm, + faces_forward, + // Rgba::from_opaque(flat_get(pos).color), + meta, + atlas_pos, + // |pos, norm, meta| create_shadow(pos, norm, meta), + // |atlas_pos, pos, norm, meta| create_opaque(atlas_pos, pos, norm, meta), + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + // z (u = x, v = y) + greedy_mesh_cross_section( + Vec3::new(greedy_size.x, greedy_size.y, greedy_size_cross.z), + |pos| { + /* if pos.z == 0 { + let pos = pos.map(|e| e as i32) + draw_delta; // - delta; + let to = flat_get(pos).is_opaque(); //map(|v| v.is_opaque()).unwrap_or(false); + if to { Some(false) } else { None } + } else */ + { + should_draw( + &mut data, + draw_delta + Vec3::new(pos.x, pos.y, pos.z), + Vec3::unit_z(), + Vec2::new(Vec3::unit_x(), Vec3::unit_y()), + ) + } + }, + |pos, dim, &(faces_forward, ref meta)| { + let pos = Vec3::new(pos.x, pos.y, pos.z); + let uv = Vec2::new(Vec3::unit_x(), Vec3::unit_y()); + let norm = Vec3::unit_z(); + // bounds.expand_to_contain(compute_bounds(pos, dim, uv)); + let atlas_pos = if let Some(atlas_pos) = add_to_atlas( + atlas, + &mut todo_rects, + pos, + uv, + dim, + norm, + faces_forward, + max_size, + col_lights_size, + ) { + atlas_pos + } else { + return; + }; + create_quad_greedy( + // &mut shadow_mesh, + // &mut opaque_mesh, + pos, + dim, + uv, + norm, + faces_forward, + // Rgba::from_opaque(flat_get(pos).color), + meta, + atlas_pos, + // |pos, norm, meta| create_shadow(pos, norm, meta), + // |atlas_pos, pos, norm, meta| create_opaque(atlas_pos, pos, norm, meta), + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + // NOTE: Safe because bound dimensions actually fit in a u16. + // let bounds = bounds.map(|e| e as u16); + // NOTE: Safe because draw_delta fits in i16. + let bounds = Aabb { + min: Vec3::zero(), + // NOTE: Safe because greedy_size fit in u16. + max: greedy_size.map(|e| e as u16), + }; + ( + bounds, + /* opaque_mesh, *//*shadow_mesh, */ + Box::new(move |col_lights_info| { + let mut data = data; + draw_col_lights( + col_lights_info, + &mut data, + todo_rects, + draw_delta, + get_light, + get_color, + get_opacity, + TerrainVertex::make_col_light, + ); + }), + ) +} + +// Greedy meshing a single cross-section. +fn greedy_mesh_cross_section( + /* mask: &mut [bool], */ + dims: Vec3, + // Should we draw a face here (below this vertex)? If so, provide its meta information. + mut draw_face: impl FnMut(Vec3) -> Option, + // Vertex, width and height, and meta information about the block. + mut push_quads: impl FnMut(Vec3, Vec2, &M), +) { + // mask represents which faces are either set while the other is unset, or unset + // while the other is set. + let mut mask = (0..dims.y * dims.x).map(|_| None).collect::>(); + (0..dims.z + 1).for_each(|d| { + // Compute mask + mask.iter_mut().enumerate().for_each(|(posi, mask)| { + let i = posi % dims.x; + let j = posi / dims.x; + // NOTE: Safe because dims.z actually fits in a u16. + *mask = draw_face(Vec3::new(i as i32, j as i32, d as i32)); + }); + + (0..dims.y).for_each(|j| { + let mut i = 0; + while i < dims.x { + // Compute width (number of set x bits for this row and layer, starting at the + // current minimum column). + if let Some(ori) = &mask[j * dims.x + i] { + let width = 1 + mask[j * dims.x + i + 1..j * dims.x + dims.x] + .iter() + .take_while(move |&mask| mask.as_ref() == Some(ori)) + .count(); + let max_x = i + width; + // Compute height (number of rows having w set x bits for this layer, starting + // at the current minimum column and row). + let height = 1 + + (j + 1..dims.y) + .take_while(|h| { + mask[h * dims.x + i..h * dims.x + max_x] + .iter() + .all(|mask| mask.as_ref() == Some(ori)) + }) + .count(); + let max_y = j + height; + // Add quad. + push_quads(Vec3::new(i, j, d /* + 1 */), Vec2::new(width, height), ori); + // Unset mask bits in drawn region, so we don't try to re-draw them. + (j..max_y).for_each(|l| { + mask[l * dims.x + i..l * dims.x + max_x] + .iter_mut() + .for_each(|mask| { + *mask = None; + }); + }); + // Update x value. + i = max_x; + } else { + i += 1; + } + } + }); + }); +} + +fn add_to_atlas( + atlas: &mut guillotiere::SimpleAtlasAllocator, + todo_rects: &mut Vec<( + Vec3, + Vec2>, + guillotiere::Rectangle, + Vec3, + )>, + pos: Vec3, + uv: Vec2>, + dim: Vec2, + norm: Vec3, + faces_forward: bool, + max_size: guillotiere::Size, + cur_size: &mut Vec2, +) -> Option { + // TODO: Check this conversion. + let atlas_rect; + loop { + // NOTE: Conversion to i32 is safe because he x, y, and z dimensions for any + // chunk index must fit in at least an i16 (lower for x and y, probably + // lower for z). + let res = atlas.allocate(guillotiere::Size::new(dim.x as i32 + 1, dim.y as i32 + 1)); + if let Some(atlas_rect_) = res { + atlas_rect = atlas_rect_; + break; + } + // Allocation failure. + let current_size = atlas.size(); + if current_size == max_size { + // NOTE: Currently, if we fail to allocate a terrain chunk in the atlas and we + // have already reached the maximum texture size, we choose to just skip the + // geometry and log a warning, rather than panicking or trying to use a fallback + // technique (e.g. a texture array). + // + // FIXME: Either make more robust, or explicitly document that limits on texture + // size need to be respected for terrain data (the OpenGL minimum requirement is + // 1024 × 1024, but in practice almost all computers support 4096 × 4096 or + // higher; see + // https://feedback.wildfiregames.com/report/opengl/feature/GL_MAX_TEXTURE_SIZE). + panic!( + "Could not add texture to atlas using simple allocator (pos={:?}, dim={:?});we \ + could not fit the whole model into a single texture on this machine + (max texture size={:?}, so we are discarding this rectangle.", + pos, dim, max_size + ); + // return None; + } + // Otherwise, we haven't reached max size yet, so double the size (or reach the + // max texture size) and try again. + let new_size = guillotiere::Size::new( + max_size.width.min(current_size.width.saturating_mul(2)), + max_size.height.min(current_size.height.saturating_mul(2)), + ); + atlas.grow(new_size); + // atlas.grow((current_size * 2).min(max_size)); + } + // NOTE: Conversion is correct because our initial max size for the atlas was + // a u16 and we never grew the atlas, meaning all valid coordinates within the + // atlas also fit into a u16. + *cur_size = Vec2::new( + cur_size.x.max(atlas_rect.max.x as u16), + cur_size.y.max(atlas_rect.max.y as u16), + ); + + /* let (dim, uv, norm) = if faces_forward { + // NOTE: Conversion to u16 safe by function precondition. + (dim.map(|e| e as u16), uv, norm) + } else { + // NOTE: Conversion to u16 safe by function precondition. + (Vec2::new(dim.y as u16, dim.x as u16), Vec2::new(uv.y, uv.x), -norm) + }; */ + + // NOTE: pos can be converted safely from usize to i32 because all legal block + // coordinates in this chunk must fit in an i32 (actually we have the much + // stronger property that this holds across the whole map). + let norm = norm.map(i32::from); + todo_rects.push(( + pos.map(|e| e as i32) + if faces_forward { -norm } else { Vec3::zero() }, + uv, + atlas_rect, + if faces_forward { norm } else { -norm }, + )); + Some(atlas_rect) +} + +/// We deferred actually recording the colors within the rectangles in order to +/// generate a texture of minimal size; we now proceed to create and populate +/// it. +/// +/// TODO: Consider using the heavier interface (not the simple one) which seems +/// to provide builtin support for what we're doing here. +fn draw_col_lights( + (col_lights, cur_size): &mut ColLightInfo, + data: &mut D, + todo_rects: Vec<( + Vec3, + Vec2>, + guillotiere::Rectangle, + Vec3, + )>, + draw_delta: Vec3, + mut get_light: impl FnMut(&mut D, Vec3) -> f32, + mut get_color: impl FnMut(&mut D, Vec3) -> Rgb, + mut get_opacity: impl FnMut(&mut D, Vec3) -> bool, + mut make_col_light: impl FnMut(u8, Rgb) -> <::Surface as gfx::format::SurfaceTyped>::DataType, +) { + /* for i in 0..todo_rects.len() { + for j in 0..todo_rects.len() { + if i == j { + continue; + } + + assert!(!todo_rects[i].2.intersects(&todo_rects[j].2)); + } + } */ + todo_rects + .into_iter() + // .rev() + .for_each(|(pos, uv, rect, delta)| { + // NOTE: Conversions are safe because width, height, and offset must be + // non-negative, and because every allocated coordinate in the atlas must be in + // bounds for the original size, max_texture_size, which fit into a u16. + let width = (rect.max.x - rect.min.x) as u16;//rect.width() as u16; + let height = (rect.max.y - rect.min.y) as u16;//rect.height() as u16; + /* if width > 32 || height > 32 { + println!("Rect: {:?}", rect); + } */ + let left = rect.min.x as u16; + let top = rect.min.y as u16; + let uv = uv.map(|e| e.map(i32::from)); + let pos = pos + draw_delta;//Vec3::new(0, 0, z_start - 1);// + mesh_delta;// + draw_delta; + (0..height).for_each(|v| { + 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)| { + let pos = pos + uv.x * i32::from(u) + uv.y * i32::from(v); + // TODO: Consider optimizing to take advantage of the fact that this whole + // face should be facing nothing but air (this is not currently true, but + // could be if we used the right AO strategy). + // Each indirect light needs to come in through the direct light. + // Thus, we assign each light a score based on opacity (currently just 0 or + // 1, but it could support transluscent lights in the future). + // Thus, indirect_u_opacity and indirect_v_opacity are multiplied by + // direct_opacity, and indirect_uv_opacity is multiplied by + // the maximum of both of u and v's indirect opacities (since there are + // two choices for how to get to the direct surface). + let pos = pos + + if u + 1 == width { -uv.x } else { Vec3::zero() } + + if v + 1 == height { -uv.y } else { Vec3::zero() }; + let uv = Vec2::new( + if u + 1 == width { -uv.x } else { uv.x }, + if v + 1 == height { -uv.y } else { uv.y }, + ); + + let light_pos = pos + /*range.min + */delta; + // let block = flat_get(pos); + + // Currently, we assume that direct_opacity is 1 (if it's 0, you can't see + // the face anyway, since it's blocked by the block directly in front of it). + // TODO: If we add non-0/1 opacities, fix this. + // top-left block + // let direct_opacity = !flat_get(pos + delta).is_opaque(); + // bottom-left block + let direct_u_opacity = get_opacity(data, light_pos - uv.x); + // top-right block + let direct_v_opacity = get_opacity(data, light_pos - uv.y); + // top-left block + // NOTE: Currently, since we only have 0 / 1 opacities, we don't worry + // about whether the uv block itself is opaque, because if it is its light + // value will be 0 anyway. But if we add translucent objects, we'll need + // to care about uv's opacity as well. + // let direct_uv_opacity = !flat_get(pos + delta - uv.x - uv.y).is_opaque(); + // let indirect_opacity = direct_uv_opacity && (direct_u_opacity || direct_v_opacity) && direct_opacity; + + // NOTE: Since we only support 0/1 opacities currently, we asssume + // direct_opacity is 1, and the light value will be zero anyway for objects + // with opacity 0, we only "multiply" by indirect_uv_opacity for now (since + // it's the only one that could be 0 even if its light value is not). + // However, "spiritually" these light values are all being multiplied by + // their opacities. + let darkness = ( + // Light from the bottom-right-front block to this vertex always + // appears on this face, since it's the block this face is facing (so + // it can't be blocked by anything). + if /*direct_u_opacity || direct_v_opacity*/true/* || !flat_get(pos - uv.x - uv.y).is_opaque()*//* || !block.is_opaque()*/ { get_light(data, light_pos) } else { 0.0 } + + if /*direct_opacity || direct_uv_opacity*/true/* || !flat_get(pos - uv.y).is_opaque()*/ { get_light(data, light_pos - uv.x) } else { 0.0 } + + if /*direct_opacity || direct_uv_opacity*/true/* || !flat_get(pos - uv.x).is_opaque()*/ { get_light(data, light_pos - uv.y) } else { 0.0 } + + if direct_u_opacity || direct_v_opacity/* || !block.is_opaque()*/ { get_light(data, light_pos - uv.x - uv.y) } else { 0.0 } + ) / 4.0; + let col = get_color(data, pos);//.map(Rgba::from_opaque).unwrap_or(Rgba::zero()); + let light = (darkness * 255.0) as u8; + *col_light = make_col_light(light, col); + }); + }); + }); +} + +/// Precondition: when this function is called, atlas_pos should reflect an +/// actual valid position in a texture atlas (meaning it should fit into a u16). +fn create_quad_greedy( + // shadow_mesh: &mut Mesh, + // opaque_mesh: &mut Mesh, + origin: Vec3, + dim: Vec2, + uv: Vec2>, + norm: Vec3, + faces_forward: bool, + meta: &M, + atlas_pos: guillotiere::Rectangle, + // origin, norm, meta + // create_shadow: impl Fn(Vec3, Vec3, &M) -> S::Vertex, + // create_opaque: impl Fn(Vec2, Vec3, Vec3, &M) -> O::Vertex, + mut push_quad: impl FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), +) /* -> Quad */ +{ + let origin = origin.map(|e| e as f32); + /* // NOTE: Conversion to u16 safe by function precondition. + let dim = uv.map2(dim.map(|e| e as u16), |e, f| e * f); */ + // NOTE: Conversion to f32 safe by function precondition (u16 can losslessly + // cast to f32, and dim fits in a u16). + let draw_dim = uv.map2(dim.map(|e| e as f32), |e, f| e.map(f32::from) * f); + let dim = Vec2::new(Vec2::new(dim.x as u16, 0), Vec2::new(0, dim.y as u16)); + let (draw_dim, dim, /* uv, */ norm) = if faces_forward { + /* // NOTE: Conversion to u16 safe by function precondition. + (dim.map(|e| e as u16), uv, norm) */ + (draw_dim, dim, norm) + } else { + /* // NOTE: Conversion to u16 safe by function precondition. + (Vec2::new(dim.y as u16, dim.x as u16), Vec2::new(uv.y, uv.x), -norm) */ + ( + Vec2::new(draw_dim.y, draw_dim.x), + Vec2::new(dim.y, dim.x), + -norm, + ) + }; + let norm = norm.map(f32::from); + // let draw_dim = draw_dim.map(|e| e.map(f32::from)); + // NOTE: Conversion to u16 safe by function precondition. + let atlas_pos = Vec2::new(atlas_pos.min.x as u16, atlas_pos.min.y as u16); + /* shadow_mesh.push_quad(Quad::new( + create_shadow(origin, norm, &meta/*, atlas_pos*/), + create_shadow(origin + draw_dim.x, norm, &meta/*, atlas_pos + dim.x*/), + create_shadow(origin + draw_dim.x + draw_dim.y, norm, &meta/*, atlas_pos + dim.x + dim.y*/), + create_shadow(origin + draw_dim.y, norm, &meta/*, atlas_pos + dim.y*/), + )); */ + /* opaque_mesh.push_quad(Quad::new( + create_opaque(atlas_pos, origin, norm, &meta), + create_opaque(atlas_pos + dim.x, origin + draw_dim.x, norm, &meta), + create_opaque(atlas_pos + dim.x + dim.y, origin + draw_dim.x + draw_dim.y, norm, &meta), + create_opaque(atlas_pos + dim.y, origin + draw_dim.y, norm, &meta), + )); */ + push_quad(atlas_pos, dim, origin, draw_dim, norm, meta); +} + +pub fn create_quad( + atlas_pos: Vec2, + dim: Vec2>, + origin: Vec3, + draw_dim: Vec2>, + norm: Vec3, + meta: &M, + create_vertex: impl Fn(Vec2, Vec3, Vec3, &M) -> O::Vertex, +) -> Quad { + Quad::new( + create_vertex(atlas_pos, origin, norm, meta), + create_vertex(atlas_pos + dim.x, origin + draw_dim.x, norm, meta), + create_vertex( + atlas_pos + dim.x + dim.y, + origin + draw_dim.x + draw_dim.y, + norm, + meta, + ), + create_vertex(atlas_pos + dim.y, origin + draw_dim.y, norm, meta), + /* create_vertex(atlas_pos, origin, norm, meta), + create_vertex(atlas_pos + dim.y, origin + draw_dim.y, norm, meta), + create_vertex(atlas_pos + dim.x + dim.y, origin + draw_dim.x + draw_dim.y, norm, meta), + create_vertex(atlas_pos + dim.x, origin + draw_dim.x, norm, meta), */ + ) +} diff --git a/voxygen/src/mesh/mod.rs b/voxygen/src/mesh/mod.rs index 4dc067ab1c..24e6774e46 100644 --- a/voxygen/src/mesh/mod.rs +++ b/voxygen/src/mesh/mod.rs @@ -1,22 +1,24 @@ +pub mod greedy; pub mod segment; pub mod terrain; -mod vol; use crate::render::{self, Mesh}; -pub trait Meshable<'a, P: render::Pipeline, T: render::Pipeline> { +pub trait Meshable { 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( - &'a self, + self, supp: Self::Supplement, ) -> ( Mesh, Mesh, Mesh, + Self::Result, ); } diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index 4e55f49430..c98a36e319 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -1,91 +1,118 @@ use crate::{ - mesh::{vol, Meshable}, - render::{self, FigurePipeline, Mesh, SpritePipeline}, + mesh::{ + greedy::{self, GreedyMesh}, + Meshable, + }, + render::{self, FigurePipeline, Mesh, ShadowPipeline, SpritePipeline, TerrainPipeline}, }; use common::{ figure::Cell, - util::{linear_to_srgb, srgb_to_linear}, vol::{BaseVol, ReadVol, SizedVol, Vox}, }; use vek::*; -type FigureVertex = ::Vertex; type SpriteVertex = ::Vertex; +type TerrainVertex = ::Vertex; -impl<'a, V: 'a> Meshable<'a, FigurePipeline, FigurePipeline> for V +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, /* TODO: Use VolIterator instead of manually iterating * &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>, * &'a V: BaseVol, */ { - type Pipeline = FigurePipeline; - type ShadowPipeline = FigurePipeline; - type Supplement = (Vec3, Vec3); + type Pipeline = TerrainPipeline; + type Result = Aabb; + type ShadowPipeline = ShadowPipeline; + type Supplement = (&'b mut GreedyMesh<'a>, Vec3, Vec3); type TranslucentPipeline = FigurePipeline; - // TODO: Make sprites cast shadows? - fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, + self, + (greedy, offs, scale): Self::Supplement, ) -> ( Mesh, Mesh, Mesh, + Self::Result, ) { - let mut mesh = Mesh::new(); + 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 vol_iter = (self.lower_bound().x..self.upper_bound().x) - .map(|i| { - (self.lower_bound().y..self.upper_bound().y).map(move |j| { - (self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k)) - }) - }) - .flatten() - .flatten() - .map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty()))); + let greedy_size = Vec3::new( + (self.upper_bound().x - self.lower_bound().x + 1) as usize, + (self.upper_bound().y - self.lower_bound().y + 1) as usize, + (self.upper_bound().z - self.lower_bound().z + 1) as usize, + ); + let greedy_size_cross = greedy_size; + let draw_delta = Vec3::new( + self.lower_bound().x, + self.lower_bound().y, + self.lower_bound().z, + ); - for (pos, vox) in vol_iter { - if let Some(col) = vox.get_color() { - vol::push_vox_verts( - &mut mesh, - faces_to_make(self, pos, true, |vox| vox.is_empty()), - offs + pos.map(|e| e as f32), - &[[[Rgba::from_opaque(col); 3]; 3]; 3], - |origin, norm, col, light, ao, _meta| { - FigureVertex::new( - origin * scale, - norm, - linear_to_srgb(srgb_to_linear(col) * light), - ao, - 0, - ) - }, - &{ - let mut ls = [[[None; 3]; 3]; 3]; - for x in 0..3 { - for y in 0..3 { - for z in 0..3 { - ls[z][y][x] = self - .get(pos + Vec3::new(x as i32, y as i32, z as i32) - 1) - .map(|v| v.is_empty()) - .unwrap_or(true) - .then_some(1.0); - } - } - } - ls - }, - ); + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 } - } + }; + let get_color = |vol: &mut V, pos: Vec3| { + vol.get(pos) + .ok() + .and_then(|vox| vox.get_color()) + .unwrap_or(Rgb::zero()) + }; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy(pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Vox::empty()) + }) + }; + let create_opaque = |atlas_pos, pos, norm, _meta| { + TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, 0) + }; - (mesh, Mesh::new(), Mesh::new()) + let mut opaque_mesh = Mesh::new(); + let bounds = greedy.push( + self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + |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, meta), + )); + }, + ); + let bounds = bounds.map(f32::from); + let bounds = Aabb { + min: (bounds.min + offs) * scale, + max: (bounds.max + offs) * scale, + } + .made_valid(); + + (opaque_mesh, Mesh::new(), Mesh::new(), bounds) } } -impl<'a, V: 'a> Meshable<'a, SpritePipeline, SpritePipeline> for V +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, /* TODO: Use VolIterator instead of manually iterating @@ -93,92 +120,172 @@ where * &'a V: BaseVol, */ { type Pipeline = SpritePipeline; - type ShadowPipeline = SpritePipeline; - type Supplement = (Vec3, Vec3); + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = (&'b mut GreedyMesh<'a>, bool); type TranslucentPipeline = SpritePipeline; - // TODO: Make sprites cast shadows? - fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, + self, + (greedy, vertical_stripes): Self::Supplement, ) -> ( Mesh, Mesh, Mesh, + (), ) { - let mut mesh = Mesh::new(); + 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 vol_iter = (self.lower_bound().x..self.upper_bound().x) - .map(|i| { - (self.lower_bound().y..self.upper_bound().y).map(move |j| { - (self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k)) - }) - }) - .flatten() - .flatten() - .map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty()))); + let greedy_size = Vec3::new( + (self.upper_bound().x - self.lower_bound().x + 1) as usize, + (self.upper_bound().y - self.lower_bound().y + 1) as usize, + (self.upper_bound().z - self.lower_bound().z + 1) as usize, + ); + assert!( + greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, + "Sprite size out of bounds: {:?} ≤ (15, 15, 63)", + greedy_size - 1 + ); + let greedy_size_cross = greedy_size; + let draw_delta = Vec3::new( + self.lower_bound().x, + self.lower_bound().y, + self.lower_bound().z, + ); - for (pos, vox) in vol_iter { - if let Some(col) = vox.get_color() { - vol::push_vox_verts( - &mut mesh, - faces_to_make(self, pos, true, |vox| vox.is_empty()), - offs + pos.map(|e| e as f32), - &[[[Rgba::from_opaque(col); 3]; 3]; 3], - |origin, norm, col, light, ao, _meta| { - SpriteVertex::new( - origin * scale, - norm, - linear_to_srgb(srgb_to_linear(col) * light), - ao, - ) - }, - &{ - let mut ls = [[[None; 3]; 3]; 3]; - for x in 0..3 { - for y in 0..3 { - for z in 0..3 { - ls[z][y][x] = self - .get(pos + Vec3::new(x as i32, y as i32, z as i32) - 1) - .map(|v| v.is_empty()) - .unwrap_or(true) - .then_some(1.0); - } - } - } - ls - }, - ); + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 } - } + }; + let get_color = |vol: &mut V, pos: Vec3| { + vol.get(pos) + .ok() + .and_then(|vox| vox.get_color()) + .unwrap_or(Rgb::zero()) + }; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Vox::empty()) + }) + }; + // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. + // let create_shadow = |pos, norm, _meta| ShadowVertex::new_figure((pos + offs) + // * scale, norm, 0); + let create_opaque = |atlas_pos, pos: Vec3, norm, _meta| { + /* if pos.x >= 15.0 || pos.y >= 15.0 || pos.z >= 63.0 { + println!("{:?}", pos); + } */ + SpriteVertex::new(atlas_pos, pos, norm /* , ao */) + }; - (mesh, Mesh::new(), Mesh::new()) + let mut opaque_mesh = Mesh::new(); + let _bounds = greedy.push( + self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + |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, meta), + )); + }, + ); + + (opaque_mesh, Mesh::new(), Mesh::new(), ()) + } +} +fn should_draw_greedy( + pos: Vec3, + delta: Vec3, + _uv: Vec2>, + flat_get: impl Fn(Vec3) -> Cell, +) -> Option<(bool, /* u8 */ ())> { + // TODO: Verify conversion. + // let pos = pos.map(|e| e as i32) + draw_delta; // - delta; + let from = flat_get(pos - delta); // map(|v| v.is_opaque()).unwrap_or(false); + let to = flat_get(pos); //map(|v| v.is_opaque()).unwrap_or(false); + let from_opaque = !from.is_empty(); + if from_opaque == !to.is_empty() { + None + } else { + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. + Some((from_opaque, ())) } } -/// Use the 6 voxels/blocks surrounding the one at the specified position -/// to detemine which faces should be drawn -fn faces_to_make( - seg: &V, +fn should_draw_greedy_ao( + vertical_stripes: bool, pos: Vec3, - error_makes_face: bool, - should_add: impl Fn(&V::Vox) -> bool, -) -> [Option<()>; 6] { - let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); - let make_face = |offset| { - let res = seg - .get(pos + offset) - .map(|v| should_add(v)) - .unwrap_or(error_makes_face); - if res { Some(()) } else { None } - }; - [ - make_face(-x), - make_face(x), - make_face(-y), - make_face(y), - make_face(-z), - make_face(z), - ] + delta: Vec3, + _uv: Vec2>, + flat_get: impl Fn(Vec3) -> Cell, +) -> Option<(bool, /* u8 */ bool)> { + // TODO: Verify conversion. + // let pos = pos.map(|e| e as i32) + draw_delta; // - delta; + let from = flat_get(pos - delta); // map(|v| v.is_opaque()).unwrap_or(false); + let to = flat_get(pos); //map(|v| v.is_opaque()).unwrap_or(false); + let from_opaque = !from.is_empty(); + if from_opaque == !to.is_empty() { + None + } else { + let faces_forward = from_opaque; + let ao = /* if delta.z != 0 { + 0u8 + } else { + (pos.z & 1) as u8 + // (((pos.x & 1) as u8) << 1) | (pos.y & 1) as u8 + }*/!vertical_stripes || /*((pos.x & 1) ^ (pos.y & 1))*/(pos.z & 1) != 0/* as u8*/; + /* let (from, delta, uv) = if faces_forward { + (pos - delta - uv.x - uv.y, delta, uv) + } else { + (pos, -delta, Vec2::new(-uv.y, -uv.x)) + }; + let ao_vertex = |from: Vec3, delta: Vec3, uv: Vec2>| { + let corner = !flat_get(from + delta - uv.x - uv.y).is_empty(); + let s1 = !flat_get(from + delta - uv.x).is_empty(); + let s2 = !flat_get(from + delta - uv.y).is_empty(); + if s1 && s2 { + 0 + } else { + 3 - (if corner { 1 } else { 0 } + if s1 { 1 } else { 0 } + if s2 { 1 } else { 0 }) + } + }; + // We only care about the vertices we are *not* merging, since the shared vertices + // by definition have the same AO values. But snce we go both down and right we end up + // needing all but the bottom right vertex. + let ao_corner = ao_vertex(from, delta, uv); + let ao1 = ao_vertex(from + uv.x, delta, uv); + let ao2 = ao_vertex(from + uv.y, delta, uv); + let ao3 = ao_vertex(from + uv.x + uv.y, delta, uv); + // NOTE: ao's 4 values correspond (from 0 to 3) to 0.25, 0.5, 0.75, 1.0. + // + // 0.0 is the None case. + let ao = (ao_corner << 6) | (ao1 << 4) | (ao2 << 2) | ao3; */ + // let ao = ao_vertex(from, delta, uv); + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. + Some((faces_forward, ao)) + // Some((faces_forward, ())) + } } diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index b30f45ebca..04cf885820 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -1,6 +1,9 @@ use crate::{ - mesh::{vol, Meshable}, - render::{self, mesh::Quad, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline}, + mesh::{ + greedy::{self, GreedyMesh}, + Meshable, + }, + render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline}, }; use common::{ terrain::{Block, BlockKind}, @@ -12,7 +15,16 @@ use vek::*; type TerrainVertex = ::Vertex; type FluidVertex = ::Vertex; -type ShadowVertex = ::Vertex; + +#[derive(Clone, Copy, PartialEq)] +enum FaceKind { + /// Opaque face that is facing something non-opaque; either + /// water (Opaque(true)) or something else (Opaque(false)). + Opaque(bool), + /// Fluid face that is facing something non-opaque, non-tangible, + /// and non-fluid (most likely air). + Fluid, +} trait Blendable { fn is_blended(&self) -> bool; @@ -200,20 +212,22 @@ fn calc_light + ReadVol + Debug>( } impl<'a, V: RectRasterableVol + ReadVol + Debug> - Meshable<'a, TerrainPipeline, FluidPipeline> for VolGrid2d + Meshable for &'a VolGrid2d { type Pipeline = TerrainPipeline; + type Result = (Aabb, ColLightInfo); type ShadowPipeline = ShadowPipeline; - type Supplement = Aabb; + type Supplement = (Aabb, Vec2); type TranslucentPipeline = FluidPipeline; fn generate_mesh( - &'a self, - range: Self::Supplement, + self, + (range, max_texture_size): Self::Supplement, ) -> ( Mesh, Mesh, Mesh, + Self::Result, ) { let mut light = calc_light(range, self); @@ -262,7 +276,7 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> 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 {}"), + None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), } } }; @@ -298,10 +312,10 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> // similar z // levels together (better rendering performance) // let mut opaque_meshes = vec![Mesh::new(); ((z_end + 1 - z_start).clamped(1, // 60) as usize / 10).max(1)]; - let mut opaque_mesh = Mesh::new(); - let mut fluid_mesh = Mesh::new(); + // let mut opaque_mesh = Mesh::new(); + // let mut fluid_mesh = Mesh::new(); - for x in 1..range.size().w - 1 { + /* for x in 1..range.size().w - 1 { for y in 1..range.size().w - 1 { let mut blocks = [[[None; 3]; 3]; 3]; for i in 0..3 { @@ -398,7 +412,7 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> // + 1 - z_start).max(1)) as usize; let selected_opaque_mesh // = &mut opaque_meshes[opaque_mesh_index]; Create mesh // polygons - if block.map_or(false, |vox| vox.is_opaque()) { + /* if block.map_or(false, |vox| vox.is_opaque()) { vol::push_vox_verts( &mut opaque_mesh, //selected_opaque_mesh, faces_to_make(&blocks, None, |vox| { @@ -425,7 +439,7 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> }, &lights, ); - } else if block.map_or(false, |vox| vox.is_fluid()) { + } else */if block.map_or(false, |vox| vox.is_fluid()) { vol::push_vox_verts( &mut fluid_mesh, // NOTE: want to skip blocks that aren't either next to air, or next to @@ -455,7 +469,7 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> } } } - } + }*/ // let opaque_mesh = opaque_meshes // .into_iter() @@ -468,286 +482,121 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> // }); // opaque_mesh // }); - - let mut shadow_mesh = Mesh::new(); - - let x_size = (range.size().w - 2) as usize; - let y_size = (range.size().h - 2) as usize; - let z_size = (z_end - z_start + 1) as usize; + let max_size = + guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y)); + let greedy_size = Vec3::new( + (range.size().w - 2) as usize, + (range.size().h - 2) as usize, + (z_end - z_start + 1) 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 mesh_delta = Vec3::new(0, 0, z_start + range.min.z); - // x (u = y, v = z) - greedy_mesh_cross_section( - Vec3::new(y_size, z_size, x_size - 1), - |pos| { - should_draw_greedy( - Vec3::new(pos.z, pos.x, pos.y), - draw_delta, - Vec3::unit_x(), /* , pos.z, 0, x_size */ - |pos| flat_get(pos), - ) - }, - |pos, dim, faces_forward| { - shadow_mesh.push_quad(create_quad_greedy( - Vec3::new(pos.z, pos.x, pos.y), - mesh_delta, - dim, - Vec2::new(Vec3::unit_y(), Vec3::unit_z()), - Vec3::unit_x(), - faces_forward, - )); + let get_light = |_: &mut (), pos: Vec3| light(pos + range.min); + let get_color = + |_: &mut (), pos: Vec3| flat_get(pos).get_color().unwrap_or(Rgb::zero()); + let get_opacity = |_: &mut (), pos: Vec3| !flat_get(pos).is_opaque(); + let should_draw = |_: &mut (), pos: Vec3, delta: Vec3, _uv| { + should_draw_greedy(pos, delta, |pos| flat_get(pos)) + }; + // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. + // let create_shadow = |pos, norm, meta| ShadowVertex::new(pos + Vec3::new(0.0, + // 0.0, (z_start + range.min.z) as f32), norm, meta); + 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, _meta| 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(); + let bounds = greedy.push( + (), + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + |atlas_origin, dim, origin, draw_dim, norm, meta| 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, meta) + }, + )); + }, }, ); - // y (u = z, v = x) - greedy_mesh_cross_section( - Vec3::new(z_size, x_size, y_size - 1), - |pos| { - should_draw_greedy( - Vec3::new(pos.y, pos.z, pos.x), - draw_delta, - Vec3::unit_y(), - |pos| flat_get(pos), - ) - }, - |pos, dim, faces_forward| { - shadow_mesh.push_quad(create_quad_greedy( - Vec3::new(pos.y, pos.z, pos.x), - mesh_delta, - dim, - Vec2::new(Vec3::unit_z(), Vec3::unit_x()), - Vec3::unit_y(), - faces_forward, - )); - }, - ); + let bounds = bounds.map(f32::from); + let bounds = Aabb { + min: bounds.min + mesh_delta, + max: bounds.max + mesh_delta, + }; + let (col_lights, col_lights_size) = greedy.finalize(); + // println!("z_bounds{:?}, bounds: {:?}", (mesh_delta.z, mesh_delta.z + - // z (u = x, v = y) - greedy_mesh_cross_section( - Vec3::new(x_size, y_size, z_size), - |pos| { - if pos.z == 0 { - let pos = pos.map(|e| e as i32) + draw_delta; // - delta; - let to = flat_get(pos).is_opaque(); //map(|v| v.is_opaque()).unwrap_or(false); - if to { Some(false) } else { None } - } else { - should_draw_greedy( - Vec3::new(pos.x, pos.y, pos.z), - draw_delta, - Vec3::unit_z(), - |pos| flat_get(pos), - ) - } - }, - |pos, dim, faces_forward| { - shadow_mesh.push_quad(create_quad_greedy( - Vec3::new(pos.x, pos.y, pos.z), - mesh_delta, - dim, - Vec2::new(Vec3::unit_x(), Vec3::unit_y()), - Vec3::unit_z(), - faces_forward, - )); - }, - ); - - (opaque_mesh, fluid_mesh, shadow_mesh) + ( + opaque_mesh, + fluid_mesh, + Mesh::new(), + (bounds, (col_lights, col_lights_size)), + ) } } -/// Use the 6 voxels/blocks surrounding the center -/// to detemine which faces should be drawn -/// Unlike the one in segments.rs this uses a provided array of blocks instead -/// of retrieving from a volume -/// blocks[z][y][x] -fn faces_to_make( - blocks: &[[[Option; 3]; 3]; 3], - error_makes_face: Option, - should_add: impl Fn(Block) -> Option, -) -> [Option; 6] { - // Faces to draw - let make_face = |opt_v: Option| { - opt_v - .map(|v| should_add(v)) - .unwrap_or(error_makes_face.clone()) - }; - [ - make_face(blocks[1][1][0]), - make_face(blocks[1][1][2]), - make_face(blocks[1][0][1]), - make_face(blocks[1][2][1]), - make_face(blocks[0][1][1]), - make_face(blocks[2][1][1]), - ] -} - -// Greedy meshing. -fn greedy_mesh_cross_section( - /* mask: &mut [bool], */ - dims: Vec3, - // Should we draw a face here (below this vertex)? If so, is it front or back facing? - draw_face: impl Fn(Vec3) -> Option, - // Vertex, width and height, and whether it's front facing (face is implicit from the cross - // section). - mut push_quads: impl FnMut(Vec3, Vec2, bool), -) { - // mask represents which faces are either set while the other is unset, or unset - // while the other is set. - let mut mask = vec![None; dims.y * dims.x]; - (0..dims.z + 1).for_each(|d| { - // Compute mask - mask.iter_mut().enumerate().for_each(|(posi, mask)| { - let i = posi % dims.x; - let j = posi / dims.x; - *mask = draw_face(Vec3::new(i, j, d)); - }); - - (0..dims.y).for_each(|j| { - let mut i = 0; - while i < dims.x { - // Compute width (number of set x bits for this row and layer, starting at the - // current minimum column). - if let Some(ori) = mask[j * dims.x + i] { - let width = 1 + mask[j * dims.x + i + 1..j * dims.x + dims.x] - .iter() - .take_while(move |&&mask| mask == Some(ori)) - .count(); - let max_x = i + width; - // Compute height (number of rows having w set x bits for this layer, starting - // at the current minimum column and row). - let height = 1 - + (j + 1..dims.y) - .take_while(|h| { - mask[h * dims.x + i..h * dims.x + max_x] - .iter() - .all(|&mask| mask == Some(ori)) - }) - .count(); - let max_y = j + height; - // Add quad. - push_quads(Vec3::new(i, j, d /* + 1 */), Vec2::new(width, height), ori); - // Unset mask bits in drawn region, so we don't try to re-draw them. - (j..max_y).for_each(|l| { - mask[l * dims.x + i..l * dims.x + max_x] - .iter_mut() - .for_each(|mask| { - *mask = None; - }); - }); - // Update x value. - i = max_x; - } else { - i += 1; - } - } - }); - }); -} - -fn create_quad_greedy( - origin: Vec3, - mesh_delta: Vec3, - dim: Vec2, - uv: Vec2>, - norm: Vec3, - faces_forward: bool, -) -> Quad { - let origin = (origin.map(|e| e as i32) + mesh_delta).map(|e| e as f32); - let dim = uv.map2(dim.map(|e| e as f32), |e, f| e * f); - let (dim, norm) = if faces_forward { - (dim, norm) - } else { - (Vec2::new(dim.y, dim.x), -norm) - }; - Quad::new( - ShadowVertex::new(origin, norm), - ShadowVertex::new(origin + dim.x, norm), - ShadowVertex::new(origin + dim.x + dim.y, norm), - ShadowVertex::new(origin + dim.y, norm), - ) -} - fn should_draw_greedy( - pos: Vec3, - draw_delta: Vec3, + pos: Vec3, delta: Vec3, flat_get: impl Fn(Vec3) -> Block, -) -> Option { - let pos = pos.map(|e| e as i32) + draw_delta; // - delta; - let from = flat_get(pos - delta).is_opaque(); // map(|v| v.is_opaque()).unwrap_or(false); - let to = flat_get(pos).is_opaque(); //map(|v| v.is_opaque()).unwrap_or(false); - if from == to { - None - } else { - // If going from transparent to opaque, forward facing; otherwise, backward - // facing. - Some(from) - } -} - -/* -impl + ReadVol + Debug> Meshable for VolGrid3d { - type Pipeline = TerrainPipeline; - type Supplement = Aabb; - - fn generate_mesh(&self, range: Self::Supplement) -> Mesh { - let mut mesh = Mesh::new(); - - let mut last_chunk_pos = self.pos_key(range.min); - let mut last_chunk = self.get_key(last_chunk_pos); - - let size = range.max - range.min; - for x in 1..size.x - 1 { - for y in 1..size.y - 1 { - for z in 1..size.z - 1 { - let pos = Vec3::new(x, y, z); - - let new_chunk_pos = self.pos_key(range.min + pos); - if last_chunk_pos != new_chunk_pos { - last_chunk = self.get_key(new_chunk_pos); - last_chunk_pos = new_chunk_pos; - } - let offs = pos.map(|e| e as f32 - 1.0); - if let Some(chunk) = last_chunk { - let chunk_pos = Self::chunk_offs(range.min + pos); - if let Some(col) = chunk.get(chunk_pos).ok().and_then(|vox| vox.get_color()) - { - let col = col.map(|e| e as f32 / 255.0); - - vol::push_vox_verts( - &mut mesh, - self, - range.min + pos, - offs, - col, - TerrainVertex::new, - false, - ); - } - } else { - if let Some(col) = self - .get(range.min + pos) - .ok() - .and_then(|vox| vox.get_color()) - { - let col = col.map(|e| e as f32 / 255.0); - - vol::push_vox_verts( - &mut mesh, - self, - range.min + pos, - offs, - col, - TerrainVertex::new, - false, - ); - } - } - } - } +) -> Option<(bool, FaceKind)> { + // TODO: Verify conversion. + // let pos = pos.map(|e| e as i32) + draw_delta; // - delta; + let from = flat_get(pos - delta); // map(|v| v.is_opaque()).unwrap_or(false); + let to = flat_get(pos); //map(|v| v.is_opaque()).unwrap_or(false); + let from_opaque = from.is_opaque(); + if from_opaque == to.is_opaque() { + // Check the interface of fluid and non-tangible non-fluids (e.g. air). + let from_fluid = from.is_fluid(); + if from_fluid == to.is_fluid() || from.is_tangible() || to.is_tangible() { + None + } else { + // While fluid is not culled, we still try to keep a consistent orientation as + // we do for land; if going from fluid to non-fluid, + // forwards-facing; otherwise, backwards-facing. + Some((from_fluid, FaceKind::Fluid)) } - mesh + } else { + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. Also, if either from or to is fluid, set the meta accordingly. + Some(( + from_opaque, + FaceKind::Opaque(if from_opaque { + to.is_fluid() + } else { + from.is_fluid() + }), + )) } } -*/ diff --git a/voxygen/src/mesh/vol.rs b/voxygen/src/mesh/vol.rs deleted file mode 100644 index 73e929778d..0000000000 --- a/voxygen/src/mesh/vol.rs +++ /dev/null @@ -1,233 +0,0 @@ -use vek::*; - -use crate::render::{ - mesh::{Mesh, Quad}, - Pipeline, -}; - -/// Given volume, position, and cardinal directions, compute each vertex's AO -/// value. `dirs` should be a slice of length 5 so that the sliding window of -/// size 2 over the slice yields each vertex' adjacent positions. -#[allow(unsafe_code)] -fn get_ao_quad( - shift: Vec3, - dirs: &[Vec3], - darknesses: &[[[Option; 3]; 3]; 3], -) -> Vec4<(f32, f32)> { - dirs.windows(2) - .map(|offs| { - let vox_opaque = |pos: Vec3| { - let pos = (pos + 1).map(|e| e as usize); - darknesses[pos.z][pos.y][pos.x].is_none() - }; - - let (s1, s2) = ( - vox_opaque(shift + offs[0]), - vox_opaque(shift + offs[1]), - /* - vol.get(pos + shift + offs[0]) - .map(&is_opaque) - .unwrap_or(false), - vol.get(pos + shift + offs[1]) - .map(&is_opaque) - .unwrap_or(false), - */ - ); - - let mut darkness = 0.0; - let mut total = 0.0f32; - for x in 0..2 { - for y in 0..2 { - let dark_pos = shift + offs[0] * x + offs[1] * y + 1; - if let Some(dark) = - darknesses[dark_pos.z as usize][dark_pos.y as usize][dark_pos.x as usize] - { - darkness += dark; - total += 1.0; - } - } - } - let darkness = darkness / total.max(1.0); - - ( - darkness, - if s1 && s2 { - 0.0 - } else { - let corner = vox_opaque(shift + offs[0] + offs[1]); - // Map both 1 and 2 neighbors to 0.5 occlusion. - if s1 || s2 || corner { 0.4 } else { 1.0 } - }, - ) - }) - .collect::>() -} - -#[allow(unsafe_code)] -fn get_col_quad(dirs: &[Vec3], cols: &[[[Rgba; 3]; 3]; 3]) -> Vec4> { - dirs.windows(2) - .map(|offs| { - let primary_col = Rgb::from(cols[1][1][1]).map(|e: u8| e as f32); - let mut color = Rgb::zero(); - let mut total = 0.0; - for x in 0..2 { - for y in 0..2 { - let col_pos = offs[0] * x + offs[1] * y + 1; - let col = unsafe { - cols.get_unchecked(col_pos.z as usize) - .get_unchecked(col_pos.y as usize) - .get_unchecked(col_pos.x as usize) - }; - if col.a > 0 { - let col = Rgb::new(col.r, col.g, col.b).map(|e| e as f32); - if Vec3::::from(primary_col).distance_squared(Vec3::from(col)) - < (0.025f32 * 256.0).powf(2.0) - { - color += col; - total += 256.0; - } - } - } - } - - color / total - }) - .collect() -} - -// Utility function -fn create_quad, Vec3, Rgb, f32, f32, &M) -> P::Vertex>( - origin: Vec3, - unit_x: Vec3, - unit_y: Vec3, - norm: Vec3, - cols: Vec4>, - darkness_ao: Vec4<(f32, f32)>, - meta: &M, - vcons: &F, -) -> Quad

{ - let darkness = darkness_ao.map(|e| e.0); - let ao = darkness_ao.map(|e| e.1); - - let ao_map = ao; - - if ao[0].min(ao[2]) < ao[1].min(ao[3]) { - Quad::new( - vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3], meta), - vcons(origin, norm, cols[0], darkness[0], ao_map[0], meta), - vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1], meta), - vcons( - origin + unit_x + unit_y, - norm, - cols[2], - darkness[2], - ao_map[2], - meta, - ), - ) - } else { - Quad::new( - vcons(origin, norm, cols[0], darkness[0], ao_map[0], meta), - vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1], meta), - vcons( - origin + unit_x + unit_y, - norm, - cols[2], - darkness[2], - ao_map[2], - meta, - ), - vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3], meta), - ) - } -} - -pub fn push_vox_verts( - mesh: &mut Mesh

, - faces: [Option; 6], - offs: Vec3, - cols: &[[[Rgba; 3]; 3]; 3], - vcons: impl Fn(Vec3, Vec3, Rgb, f32, f32, &M) -> P::Vertex, - darknesses: &[[[Option; 3]; 3]; 3], -) { - let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); - - // -x - if let Some(meta) = &faces[0] { - mesh.push_quad(create_quad( - offs, - Vec3::unit_z(), - Vec3::unit_y(), - -Vec3::unit_x(), - get_col_quad(&[-z, -y, z, y, -z], cols), - get_ao_quad(-Vec3::unit_x(), &[-z, -y, z, y, -z], darknesses), - meta, - &vcons, - )); - } - // +x - if let Some(meta) = &faces[1] { - mesh.push_quad(create_quad( - offs + Vec3::unit_x(), - Vec3::unit_y(), - Vec3::unit_z(), - Vec3::unit_x(), - get_col_quad(&[-y, -z, y, z, -y], cols), - get_ao_quad(Vec3::unit_x(), &[-y, -z, y, z, -y], darknesses), - meta, - &vcons, - )); - } - // -y - if let Some(meta) = &faces[2] { - mesh.push_quad(create_quad( - offs, - Vec3::unit_x(), - Vec3::unit_z(), - -Vec3::unit_y(), - get_col_quad(&[-x, -z, x, z, -x], cols), - get_ao_quad(-Vec3::unit_y(), &[-x, -z, x, z, -x], darknesses), - meta, - &vcons, - )); - } - // +y - if let Some(meta) = &faces[3] { - mesh.push_quad(create_quad( - offs + Vec3::unit_y(), - Vec3::unit_z(), - Vec3::unit_x(), - Vec3::unit_y(), - get_col_quad(&[-z, -x, z, x, -z], cols), - get_ao_quad(Vec3::unit_y(), &[-z, -x, z, x, -z], darknesses), - meta, - &vcons, - )); - } - // -z - if let Some(meta) = &faces[4] { - mesh.push_quad(create_quad( - offs, - Vec3::unit_y(), - Vec3::unit_x(), - -Vec3::unit_z(), - get_col_quad(&[-y, -x, y, x, -y], cols), - get_ao_quad(-Vec3::unit_z(), &[-y, -x, y, x, -y], darknesses), - meta, - &vcons, - )); - } - // +z - if let Some(meta) = &faces[5] { - mesh.push_quad(create_quad( - offs + Vec3::unit_z(), - Vec3::unit_x(), - Vec3::unit_y(), - Vec3::unit_z(), - get_col_quad(&[-x, -y, x, y, -x], cols), - get_ao_quad(Vec3::unit_z(), &[-x, -y, x, y, -x], darknesses), - meta, - &vcons, - )); - } -} diff --git a/voxygen/src/render/consts.rs b/voxygen/src/render/consts.rs index f78ec24dc5..bb8b6b3150 100644 --- a/voxygen/src/render/consts.rs +++ b/voxygen/src/render/consts.rs @@ -17,14 +17,24 @@ impl Consts { } } + /* /// Create a new immutable `Const`. + pub fn new_immutable(factory: &mut gfx_backend::Factory, data: &[T]) -> Result, RenderError> { + Ok(Self { + ibuf: factory + .create_buffer_immutable_raw(gfx::memory::cast_slice(data), core::mem::size_of::(), gfx::buffer::Role::Constant, gfx::memory::Bind::empty()) + .map_err(|err| RenderError::BufferCreationError(err))?, + }) + } */ + /// Update the GPU-side value represented by this constant handle. pub fn update( &mut self, encoder: &mut gfx::Encoder, vals: &[T], + offset: usize, ) -> Result<(), RenderError> { encoder - .update_buffer(&self.buf, vals, 0) + .update_buffer(&self.buf, vals, offset) .map_err(|err| RenderError::UpdateError(err)) } } diff --git a/voxygen/src/render/mesh.rs b/voxygen/src/render/mesh.rs index 3112098d63..b835cd6f76 100644 --- a/voxygen/src/render/mesh.rs +++ b/voxygen/src/render/mesh.rs @@ -4,6 +4,7 @@ use std::iter::FromIterator; /// A `Vec`-based mesh structure used to store mesh data on the CPU. pub struct Mesh { verts: Vec, + // textures: Vec<> } impl Clone for Mesh

diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 946b780da3..1e462a5624 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -16,7 +16,10 @@ pub use self::{ mesh::{Mesh, Quad, Tri}, model::{DynamicModel, Model}, pipelines::{ - figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals}, + figure::{ + BoneData as FigureBoneData, BoneMeshes, FigureModel, FigurePipeline, + Locals as FigureLocals, + }, fluid::FluidPipeline, lod_terrain::{Locals as LodTerrainLocals, LodTerrainPipeline}, postprocess::{ @@ -24,7 +27,7 @@ pub use self::{ }, shadow::{Locals as ShadowLocals, ShadowPipeline}, skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline}, - sprite::{Instance as SpriteInstance, SpritePipeline}, + sprite::{Instance as SpriteInstance, Locals as SpriteLocals, SpritePipeline}, terrain::{Locals as TerrainLocals, TerrainPipeline}, ui::{ create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals, @@ -33,8 +36,8 @@ pub use self::{ Globals, Light, Shadow, }, renderer::{ - LodColorFmt, LodTextureFmt, Renderer, ShadowDepthStencilFmt, TgtColorFmt, - TgtDepthStencilFmt, WinColorFmt, WinDepthFmt, + ColLightFmt, ColLightInfo, LodColorFmt, LodTextureFmt, Renderer, ShadowDepthStencilFmt, + TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt, }, texture::Texture, }; @@ -72,6 +75,10 @@ pub enum AaMode { SsaaX4, } +impl Default for AaMode { + fn default() -> Self { AaMode::Fxaa } +} + /// Cloud modes #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum CloudMode { @@ -79,6 +86,10 @@ pub enum CloudMode { Regular, } +impl Default for CloudMode { + fn default() -> Self { CloudMode::Regular } +} + /// Fluid modes #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum FluidMode { @@ -86,6 +97,10 @@ pub enum FluidMode { Shiny, } +impl Default for FluidMode { + fn default() -> Self { FluidMode::Shiny } +} + /// Lighting modes #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum LightingMode { @@ -93,3 +108,30 @@ pub enum LightingMode { BlinnPhong, Lambertian, } + +impl Default for LightingMode { + fn default() -> Self { LightingMode::BlinnPhong } +} + +/// Shadow modes +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum ShadowMode { + None, + Cheap, + /// Multiple of resolution. + Map, /* (f32) */ +} + +impl Default for ShadowMode { + fn default() -> Self { ShadowMode::Cheap } +} + +/// Render modes +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, Serialize, Deserialize)] +pub struct RenderMode { + pub aa: AaMode, + pub cloud: CloudMode, + pub fluid: FluidMode, + pub lighting: LightingMode, + pub shadow: ShadowMode, +} diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index e2e22a972d..5938954859 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -1,11 +1,14 @@ use super::{ - super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + super::{ + util::arr_to_mat, ColLightFmt, Mesh, Model, Pipeline, TerrainPipeline, Texture, + TgtColorFmt, TgtDepthStencilFmt, + }, + shadow, Globals, Light, Shadow, }; +use crate::mesh::greedy::GreedyMesh; use gfx::{ self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, - gfx_pipeline_inner, gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask, }; use vek::*; @@ -22,15 +25,21 @@ gfx_defines! { constant Locals { model_mat: [[f32; 4]; 4] = "model_mat", model_col: [f32; 4] = "model_col", + atlas_offs: [i32; 4] = "atlas_offs", + model_pos: [f32; 3] = "model_pos", flags: u32 = "flags", } constant BoneData { bone_mat: [[f32; 4]; 4] = "bone_mat", + normals_mat: [[f32; 4]; 4] = "normals_mat", } pipeline pipe { - vbuf: gfx::VertexBuffer = (), + // vbuf: gfx::VertexBuffer = (), + vbuf: gfx::VertexBuffer<::Vertex> = (), + // abuf: gfx::VertexBuffer<::Vertex> = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", @@ -38,15 +47,20 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", - shadow_maps: gfx::TextureSampler = "t_shadow_maps", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", map: gfx::TextureSampler<[f32; 4]> = "t_map", horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Replace))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Replace))), } } @@ -80,30 +94,49 @@ impl Vertex { } impl Locals { - pub fn new(model_mat: Mat4, col: Rgba, is_player: bool) -> Self { + pub fn new( + model_mat: Mat4, + col: Rgba, + pos: Vec3, + atlas_offs: Vec2, + is_player: bool, + ) -> Self { let mut flags = 0; flags |= is_player as u32; Self { model_mat: arr_to_mat(model_mat.into_col_array()), model_col: col.into_array(), + model_pos: pos.into_array(), + atlas_offs: Vec4::from(atlas_offs).into_array(), flags, } } } impl Default for Locals { - fn default() -> Self { Self::new(Mat4::identity(), Rgba::broadcast(1.0), false) } + fn default() -> Self { + Self::new( + Mat4::identity(), + Rgba::broadcast(1.0), + Vec3::default(), + Vec2::default(), + false, + ) + } } impl BoneData { - pub fn new(bone_mat: Mat4) -> Self { + pub fn new(bone_mat: Mat4, normals_mat: Mat4) -> Self { Self { bone_mat: arr_to_mat(bone_mat.into_col_array()), + normals_mat: arr_to_mat(normals_mat.into_col_array()), } } +} - pub fn default() -> Self { Self::new(Mat4::identity()) } +impl Default for BoneData { + fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) } } pub struct FigurePipeline; @@ -111,3 +144,30 @@ pub struct FigurePipeline; impl Pipeline for FigurePipeline { type Vertex = Vertex; } + +pub struct FigureModel { + pub bounds: Aabb, + pub opaque: Model, + // pub shadow: Model, + // TODO: Consider using mipmaps instead of storing multiple texture atlases for different LOD + // levels. + pub col_lights: Texture, + pub allocation: guillotiere::Allocation, +} + +impl FigureModel { + /// Start a greedy mesh designed for figure bones. + pub fn make_greedy<'a>() -> GreedyMesh<'a> { + // 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. + let max_size = guillotiere::Size::new(1 << 15 - 1, 1 << 15 - 1); + GreedyMesh::new(max_size) + } +} + +pub type BoneMeshes = ( + Mesh, /* , Mesh */ + Aabb, +); diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs index 30057c63ba..fd3e1bce57 100644 --- a/voxygen/src/render/pipelines/fluid.rs +++ b/voxygen/src/render/pipelines/fluid.rs @@ -1,19 +1,17 @@ use super::{ super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + shadow, Globals, Light, Shadow, }; use gfx::{ self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, - gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + gfx_vertex_struct_meta, state::ColorMask, }; -use std::ops::Mul; use vek::*; gfx_defines! { vertex Vertex { pos_norm: u32 = "v_pos_norm", - col_light: u32 = "v_col_light", + // col_light: u32 = "v_col_light", } pipeline pipe { @@ -24,7 +22,8 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", - shadow_maps: gfx::TextureSampler = "t_shadow_maps", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", map: gfx::TextureSampler<[f32; 4]> = "t_map", horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", @@ -32,13 +31,17 @@ gfx_defines! { noise: gfx::TextureSampler = "t_noise", waves: gfx::TextureSampler<[f32; 4]> = "t_waves", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_TEST,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_TEST,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } impl Vertex { - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, light: f32, _opac: f32) -> Self { + /* pub fn new(pos: Vec3, norm: Vec3, col: Rgb, light: f32, _opac: f32) -> Self { let (norm_axis, norm_dir) = norm .as_slice() .into_iter() @@ -62,6 +65,31 @@ impl Vertex { | ((light.mul(255.0) as u32) & 0xFF) << 0, //| ((opac.mul(0.4) as u32) & 0xFF) << 0, } + } */ + pub fn new(pos: Vec3, norm: Vec3) -> Self { + let (norm_axis, norm_dir) = norm + .as_slice() + .into_iter() + .enumerate() + .find(|(_i, e)| **e != 0.0) + .unwrap_or((0, &1.0)); + let norm_bits = ((norm_axis << 1) | if *norm_dir > 0.0 { 1 } else { 0 }) as u32; + + const EXTRA_NEG_Z: f32 = 65536.0; + + Self { + pos_norm: 0 + | ((pos.x as u32) & 0x003F) << 0 + | ((pos.y as u32) & 0x003F) << 6 + | (((pos.z + EXTRA_NEG_Z).max(0.0).min((1 << 17) as f32) as u32) & 0x1FFFF) << 12 + | (norm_bits & 0x7) << 29, + /* col_light: 0 + | ((col.r.mul(200.0) as u32) & 0xFF) << 8 + | ((col.g.mul(200.0) as u32) & 0xFF) << 16 + | ((col.b.mul(200.0) as u32) & 0xFF) << 24 + | ((light.mul(255.0) as u32) & 0xFF) << 0, + //| ((opac.mul(0.4) as u32) & 0xFF) << 0, */ + } } } diff --git a/voxygen/src/render/pipelines/lod_terrain.rs b/voxygen/src/render/pipelines/lod_terrain.rs index 32172b938f..c78f7de970 100644 --- a/voxygen/src/render/pipelines/lod_terrain.rs +++ b/voxygen/src/render/pipelines/lod_terrain.rs @@ -5,7 +5,6 @@ use super::{ use gfx::{ self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, gfx_vertex_struct_meta, - state::{Comparison, Stencil, StencilOp}, }; use vek::*; @@ -29,7 +28,8 @@ gfx_defines! { noise: gfx::TextureSampler = "t_noise", tgt_color: gfx::RenderTarget = "tgt_color", - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 15305bbd2e..dc45628ba4 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -14,7 +14,7 @@ use common::terrain::BlockKind; use gfx::{self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta}; use vek::*; -pub const MAX_POINT_LIGHT_COUNT: usize = 32; +pub const MAX_POINT_LIGHT_COUNT: usize = 31; pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; @@ -24,6 +24,7 @@ gfx_defines! { 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: max_intensity is computed as the ratio between the brightest and least bright /// intensities among all lights in the scene. @@ -36,6 +37,8 @@ gfx_defines! { /// 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. @@ -83,14 +86,24 @@ impl Globals { cam_mode: CameraMode, sprite_render_distance: f32, ) -> Self { + // Transform to left-handed homogeneous coordinates. + let proj_mat_lh = proj_mat; + // proj_mat_lh[(2, 2)] = -proj_mat[(2, 2)]; + // proj_mat_lh[(3, 2)] = -proj_mat[(3, 2)]; Self { view_mat: arr_to_mat(view_mat.into_col_array()), proj_mat: arr_to_mat(proj_mat.into_col_array()), - all_mat: arr_to_mat((proj_mat * view_mat).into_col_array()), + all_mat: arr_to_mat( + ((proj_mat_lh * view_mat)/* .scaled_3d(Vec3::new(0.0, 0.0, -1.0)) */) + .into_col_array(), + ), cam_pos: Vec4::from(cam_pos).into_array(), - focus_pos: Vec4::from(focus_pos).into_array(), + focus_off: Vec4::from(focus_pos).map(|e: f32| e.trunc()).into_array(), + focus_pos: Vec4::from(focus_pos).map(|e: f32| e.fract()).into_array(), view_distance: [view_distance, tgt_detail, map_bounds.x, map_bounds.y], time_of_day: [time_of_day as f32; 4], + sun_dir: Vec4::from_direction(Self::get_sun_dir(time_of_day)).into_array(), + moon_dir: Vec4::from_direction(Self::get_moon_dir(time_of_day)).into_array(), tick: [tick as f32; 4], // Provide the shadow map far plane as well. screen_res: [ @@ -100,9 +113,9 @@ impl Globals { shadow_planes.y, ], light_shadow_count: [ - (light_count % MAX_POINT_LIGHT_COUNT) as u32, - (shadow_count % MAX_FIGURE_SHADOW_COUNT) as u32, - (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: [ @@ -121,6 +134,21 @@ impl Globals { sprite_render_distance, } } + + fn get_angle_rad(time_of_day: f64) -> f32 { + const TIME_FACTOR: f32 = (std::f32::consts::PI * 2.0) / (3600.0 * 24.0); + time_of_day as f32 * TIME_FACTOR + } + + pub fn get_sun_dir(time_of_day: f64) -> Vec3 { + let angle_rad = Self::get_angle_rad(time_of_day); + Vec3::new(angle_rad.sin(), 0.0, angle_rad.cos()) + } + + pub fn get_moon_dir(time_of_day: f64) -> Vec3 { + let angle_rad = Self::get_angle_rad(time_of_day); + -Vec3::new(angle_rad.sin(), 0.0, angle_rad.cos() - 0.5) + } } impl Default for Globals { diff --git a/voxygen/src/render/pipelines/shadow.rs b/voxygen/src/render/pipelines/shadow.rs index 0f8d23e4b4..a648f353e1 100644 --- a/voxygen/src/render/pipelines/shadow.rs +++ b/voxygen/src/render/pipelines/shadow.rs @@ -1,6 +1,9 @@ use super::{ - super::{util::arr_to_mat, Pipeline, ShadowDepthStencilFmt, TerrainLocals}, - Globals, Light, Shadow, + super::{ + util::arr_to_mat, 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, @@ -13,35 +16,70 @@ gfx_defines! { // pos: [f32; 4] = "v_pos", pos_norm: u32 = "v_pos_norm", // col_light: u32 = "v_col_light", + // atlas_pos: u32 = "v_atlas_pos", } constant Locals { shadow_matrices: [[f32; 4]; 4] = "shadowMatrices", + texture_mats: [[f32; 4]; 4] = "texture_mat", } pipeline pipe { // Terrain vertex stuff - vbuf: gfx::VertexBuffer = (), + vbuf: gfx::VertexBuffer = (), locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", - lights: gfx::ConstantBuffer = "u_lights", - shadows: gfx::ConstantBuffer = "u_shadows", + // lights: gfx::ConstantBuffer = "u_lights", + // shadows: gfx::ConstantBuffer = "u_shadows", - map: gfx::TextureSampler<[f32; 4]> = "t_map", - horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + // map: gfx::TextureSampler<[f32; 4]> = "t_map", + // horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", - noise: gfx::TextureSampler = "t_noise", + // noise: gfx::TextureSampler = "t_noise", // Shadow stuff light_shadows: gfx::ConstantBuffer = "u_light_shadows", - tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE,//,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + // tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE,//,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } + + pipeline figure_pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + bones: gfx::ConstantBuffer = "u_bones", + globals: gfx::ConstantBuffer = "u_globals", + // lights: gfx::ConstantBuffer = "u_lights", + // shadows: gfx::ConstantBuffer = "u_shadows", + + // map: gfx::TextureSampler<[f32; 4]> = "t_map", + // horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + // noise: gfx::TextureSampler = "t_noise", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + // tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_WRITE,//,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } impl Vertex { - pub fn new(pos: Vec3, norm: Vec3) -> Self { + pub fn new( + pos: Vec3, + norm: Vec3, + meta: bool, /* , atlas_pos: Vec2 */ + ) -> Self { let norm_bits = if norm.x != 0.0 { if norm.x < 0.0 { 0 } else { 1 } } else if norm.y != 0.0 { @@ -52,7 +90,7 @@ impl Vertex { // let ao = 0xFFu32; // let light = 0xFFu32; // let col = Rgb::new(1.0f32, 0.0, 0.0); - let meta = true; + // let meta = true; const EXTRA_NEG_Z: f32 = 32768.0; @@ -63,6 +101,9 @@ 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, + /* atlas_pos: 0 + | ((atlas_pos.x as u32) & 0xFFFF) << 0 + | ((atlas_pos.y as u32) & 0xFFFF) << 16, */ /* col_light: 0 | (((col.r * 255.0) as u32) & 0xFF) << 8 | (((col.g * 255.0) as u32) & 0xFF) << 16 @@ -71,20 +112,73 @@ impl Vertex { | ((light >> 2) & 0x3F) << 0, */ } } -} -impl Locals { - pub fn new(shadow_mat: Mat4) -> Self { + pub fn new_figure( + pos: Vec3, + norm: Vec3, + /* col: Rgb, ao: f32, */ bone_idx: u8, + ) -> Self { + let norm_bits = if norm.x.min(norm.y).min(norm.z) < 0.0 { + 0 + } else { + 1 + }; Self { - shadow_matrices: arr_to_mat(shadow_mat.into_col_array()), + pos_norm: pos + .map2(Vec3::new(0, 9, 18), |e, shift| { + (((e * 2.0 + 256.0) as u32) & 0x1FF) << shift + }) + .reduce_bitor() + | (((bone_idx & 0xF) as u32) << 27) + | (norm_bits << 31), + // col: col + // .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift) + // .reduce_bitor(), + // ao_bone: (bone_idx << 2) | ((ao * 3.9999) as u8), } } - pub fn default() -> Self { Self::new(Mat4::identity()) } + pub fn with_bone_idx(self, bone_idx: u8) -> Self { + Self { + pos_norm: (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27), + } + } +} + +impl Locals { + pub fn new(shadow_mat: Mat4, texture_mat: Mat4) -> Self { + Self { + shadow_matrices: arr_to_mat(shadow_mat.into_col_array()), + texture_mats: arr_to_mat(texture_mat.into_col_array()), + } + } + + pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) } } pub struct ShadowPipeline; +impl ShadowPipeline { + pub fn create_col_lights( + renderer: &mut Renderer, + (col_lights, col_lights_size): ColLightInfo, + ) -> Result, 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 /* .raw_pixels() */], + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) + } +} + impl Pipeline for ShadowPipeline { type Vertex = Vertex; } diff --git a/voxygen/src/render/pipelines/skybox.rs b/voxygen/src/render/pipelines/skybox.rs index ead0d4b627..c9b66c84bb 100644 --- a/voxygen/src/render/pipelines/skybox.rs +++ b/voxygen/src/render/pipelines/skybox.rs @@ -5,7 +5,6 @@ use super::{ use gfx::{ self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, gfx_vertex_struct_meta, - state::{Comparison, Stencil, StencilOp}, }; gfx_defines! { @@ -29,7 +28,9 @@ gfx_defines! { noise: gfx::TextureSampler = "t_noise", tgt_color: gfx::RenderTarget = "tgt_color", - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + // tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } diff --git a/voxygen/src/render/pipelines/sprite.rs b/voxygen/src/render/pipelines/sprite.rs index 6544988ec7..b689a9e669 100644 --- a/voxygen/src/render/pipelines/sprite.rs +++ b/voxygen/src/render/pipelines/sprite.rs @@ -1,56 +1,104 @@ use super::{ super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + shadow, terrain, Globals, Light, Shadow, }; +use core::fmt; use gfx::{ - self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, - gfx_vertex_struct_meta, - state::{ColorMask, Comparison, Stencil, StencilOp}, + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask, }; use vek::*; gfx_defines! { vertex Vertex { pos: [f32; 3] = "v_pos", + // pos_norm: u32 = "v_pos_norm", + // 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", + // col: u32 = "v_col", // ...AANNN // A = AO // N = Normal norm_ao: u32 = "v_norm_ao", } - vertex Instance { + 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_col: [f32; 3] = "inst_col", + // inst_mat: [[f32; 4]; 4] = "inst_mat", + // inst_col: [f32; 3] = "inst_col", inst_wind_sway: f32 = "inst_wind_sway", } pipeline pipe { vbuf: gfx::VertexBuffer = (), ibuf: gfx::InstanceBuffer = (), + // ibuf: gfx::/*handle::RawBuffer*/ConstantBuffer = "u_ibuf", + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", + locals: gfx::ConstantBuffer = "u_locals", + // A sprite instance is a cross between a sprite and a terrain chunk. + terrain_locals: gfx::ConstantBuffer = "u_terrain_locals", + // locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", - shadow_maps: gfx::TextureSampler = "t_shadow_maps", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", map: gfx::TextureSampler<[f32; 4]> = "t_map", horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } +} + +impl fmt::Display for Vertex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Vertex") + .field("pos", &Vec3::::from(self.pos)) + .field( + "atlas_pos", + &Vec2::new( + (self.atlas_pos >> 0) & 0xFFFF, + (self.atlas_pos >> 16) & 0xFFFF, + ), + ) + .field("norm_ao", &self.norm_ao) + .finish() } } impl Vertex { - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32) -> Self { + // NOTE: Limit to 16 (x) × 16 (y) × 32 (z). + pub fn new( + atlas_pos: Vec2, + pos: Vec3, + norm: Vec3, /* , col: Rgb, 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 { @@ -60,31 +108,75 @@ impl Vertex { }; Self { + // pos_norm: 0 + // | ((pos.x as u32) & 0x003F) << 0 + // | ((pos.y as u32) & 0x003F) << 6 + // | (((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: pos.into_array(), - col: col - .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift) - .reduce_bitor(), - norm_ao: norm_bits | (((ao * 3.9999) as u32) << 3), + /* col: col + .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift) + .reduce_bitor(), */ + atlas_pos: 0 + | ((atlas_pos.x as u32) & 0xFFFF) << 0 + | ((atlas_pos.y as u32) & 0xFFFF) << 16, /* | axis_bits & 3 */ + norm_ao: norm_bits, /* | (((ao * 3.9999) as u32) << 3) */ } } } impl Instance { - pub fn new(mat: Mat4, col: Rgb, wind_sway: f32) -> Self { + pub fn new( + mat: Mat4, + /* col: Rgb, */ wind_sway: f32, + pos: Vec3, + ori_bits: u8, + ) -> Self { + const EXTRA_NEG_Z: i32 = 32768; + let mat_arr = arr_to_mat(mat.into_col_array()); Self { + pos_ori: 0 + | ((pos.x as u32) & 0x003F) << 0 + | ((pos.y as u32) & 0x003F) << 6 + | (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16/* as f32*/) as u32) & 0xFFFF) << 12 + // | if meta { 1 } else { 0 } << 28 + | (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], - inst_col: col.into_array(), + // inst_mat: mat_arr, + // inst_col: col.into_array(), inst_wind_sway: wind_sway, } } } impl Default for Instance { - fn default() -> Self { Self::new(Mat4::identity(), Rgb::broadcast(1.0), 0.0) } + fn default() -> Self { + Self::new( + Mat4::identity(), + /* Rgb::broadcast(1.0), */ 0.0, + Vec3::zero(), + 0, + ) + } +} + +impl Default for Locals { + fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) } +} + +impl Locals { + pub fn new(mat: Mat4, scale: Vec3, offs: Vec3, wind_sway: f32) -> Self { + Self { + mat: arr_to_mat(mat.into_col_array()), + wind_sway: [scale.x, scale.y, scale.z, wind_sway], + offs: [offs.x, offs.y, offs.z, 0.0], + } + } } pub struct SpritePipeline; diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index 1da19b8080..2c1cf86a3e 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -1,48 +1,125 @@ use super::{ - super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + 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, - state::{Comparison, Stencil, StencilOp}, }; -use std::ops::Mul; use vek::*; gfx_defines! { vertex Vertex { + // pos_norm: u32 = "v_pos_norm", + // col_light: u32 = "v_col_light", pos_norm: u32 = "v_pos_norm", - col_light: u32 = "v_col_light", + 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 = (), + vbuf: gfx::VertexBuffer = (), + // abuf: gfx::VertexBuffer = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", - shadow_maps: gfx::TextureSampler = "t_shadow_maps", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", map: gfx::TextureSampler<[f32; 4]> = "t_map", horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", noise: gfx::TextureSampler = "t_noise", + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + tgt_color: gfx::RenderTarget = "tgt_color", - tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } impl Vertex { - pub fn new( + pub fn new(atlas_pos: Vec2, pos: Vec3, norm: Vec3, meta: bool) -> Self { + const EXTRA_NEG_Z: f32 = 32768.0; + + let norm_bits = if norm.x != 0.0 { + if norm.x < 0.0 { 0 } else { 1 } + } else if norm.y != 0.0 { + if norm.y < 0.0 { 2 } else { 3 } + } else { + if norm.z < 0.0 { 4 } else { 5 } + }; + Self { + pos_norm: 0 + | ((pos.x as u32) & 0x003F) << 0 + | ((pos.y as u32) & 0x003F) << 6 + | (((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, + atlas_pos: 0 + | ((atlas_pos.x as u32) & 0xFFFF) << 0 + | ((atlas_pos.y as u32) & 0xFFFF) << 16, + /* col_light: 0 + | (((col.r * 255.0) as u32) & 0xFF) << 8 + | (((col.g * 255.0) as u32) & 0xFF) << 16 + | (((col.b * 255.0) as u32) & 0xFF) << 24 + | (ao >> 6) << 6 + | ((light >> 2) & 0x3F) << 0, */ + } + } + + pub fn new_figure( + // norm: Vec3, + atlas_pos: Vec2, + pos: Vec3, + norm: Vec3, + bone_idx: u8, + ) -> Self { + let norm_bits = if norm.x.min(norm.y).min(norm.z) < 0.0 { + 0 + } else { + 1 + }; + let axis_bits = if norm.x != 0.0 { + 0 + } else if norm.y != 0.0 { + 1 + } else { + 2 + }; + Self { + pos_norm: pos + .map2(Vec3::new(0, 9, 18), |e, shift| { + (((e * 2.0 + 256.0) as u32) & 0x1FF) << shift + }) + .reduce_bitor() + | (((bone_idx & 0xF) as u32) << 27) + | (norm_bits << 31), + atlas_pos: 0 + | ((atlas_pos.x as u32) & 0x7FFF) << 2 + | ((atlas_pos.y as u32) & 0x7FFF) << 17 + | axis_bits & 3, + /* col_light: 0 + | (((col.r * 255.0) as u32) & 0xFF) << 8 + | (((col.g * 255.0) as u32) & 0xFF) << 16 + | (((col.b * 255.0) as u32) & 0xFF) << 24 + | (ao >> 6) << 6 + | ((light >> 2) & 0x3F) << 0, */ + } + } + + /* pub fn new( norm_bits: u32, light: u32, ao: u32, @@ -66,6 +143,31 @@ impl Vertex { | (ao >> 6) << 6 | ((light >> 2) & 0x3F) << 0, } + } */ + + pub fn make_col_light( + light: /* u32 */ u8, + // ao: u32, + // col: Rgb, + col: Rgb, + ) -> <::Surface as gfx::format::SurfaceTyped>::DataType + { + [ + col.r, //.mul(255.0) as u8, + col.g, //.mul(255.0) as u8, + col.b, //.mul(255.0) as u8, + light, + /* | (ao as u8 >> 6) << 6 + * | //((light as u8 >> 2) & 0x3F) << 0, + * | light */ + ] + } + + pub fn with_bone_idx(self, bone_idx: u8) -> Self { + Self { + pos_norm: (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27), + ..self + } } } @@ -74,6 +176,7 @@ impl Locals { Self { model_offs: [0.0; 3], load_time: 0.0, + atlas_offs: [0; 4], } } } @@ -81,5 +184,5 @@ impl Locals { pub struct TerrainPipeline; impl Pipeline for TerrainPipeline { - type Vertex = Vertex; + type Vertex = Vertex; //<::Surface as gfx::format::SurfaceTyped>::DataType; } diff --git a/voxygen/src/render/pipelines/ui.rs b/voxygen/src/render/pipelines/ui.rs index 35ba91bb86..782f07f118 100644 --- a/voxygen/src/render/pipelines/ui.rs +++ b/voxygen/src/render/pipelines/ui.rs @@ -130,6 +130,18 @@ pub fn create_quad( v([l, b], [uv_l, uv_b]), v([r, b], [uv_r, uv_b]), ), + /* (true, true) | (false, false) => Quad::new( + v([l, t], [uv_l, uv_t]), + v([r, t], [uv_l, uv_b]), + v([r, b], [uv_r, uv_b]), + v([l, b], [uv_r, uv_t]), + ), + _ => Quad::new( + v([l, t], [uv_l, uv_t]), + v([l, b], [uv_l, uv_b]), + v([r, b], [uv_r, uv_b]), + v([r, t], [uv_r, uv_t]), + ) */ } } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 19c9b96b99..4b4363819c 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -9,13 +9,15 @@ use super::{ Light, Shadow, }, texture::Texture, - AaMode, CloudMode, FilterMethod, FluidMode, LightingMode, Pipeline, RenderError, WrapMode, + AaMode, CloudMode, FilterMethod, FluidMode, LightingMode, Pipeline, RenderError, RenderMode, + ShadowMode, WrapMode, }; use common::assets::{self, watch::ReloadIndicator}; +use core::convert::TryFrom; use gfx::{ self, handle::Sampler, - state::{Comparison, Stencil, StencilOp}, + state::Comparison, traits::{Device, Factory, FactoryExt}, }; use glsl_include::Context as IncludeContext; @@ -23,9 +25,9 @@ use log::error; use vek::*; /// Represents the format of the pre-processed color target. -pub type TgtColorFmt = gfx::format::Rgba16F; +pub type TgtColorFmt = gfx::format::Srgba8; /// Represents the format of the pre-processed depth and stencil target. -pub type TgtDepthStencilFmt = gfx::format::DepthStencil; +pub type TgtDepthStencilFmt = gfx::format::Depth; /// Represents the format of the window's color target. pub type WinColorFmt = gfx::format::Srgba8; @@ -33,7 +35,7 @@ pub type WinColorFmt = gfx::format::Srgba8; pub type WinDepthFmt = gfx::format::Depth; /// Represents the format of the pre-processed shadow depth target. -pub type ShadowDepthStencilFmt = gfx::format::Depth32F; +pub type ShadowDepthStencilFmt = gfx::format::Depth; /// A handle to a pre-processed color target. pub type TgtColorView = gfx::handle::RenderTargetView; @@ -46,12 +48,15 @@ pub type WinColorView = gfx::handle::RenderTargetView; -/// Represents the format of LOD shadow targets. +/// Represents the format of LOD shadows. pub type LodTextureFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Unorm); //[gfx::format::U8Norm; 4]; -/// Represents the format of LOD map color targets. +/// Represents the format of LOD map colors. pub type LodColorFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); //[gfx::format::U8Norm; 4]; +/// Represents the format of greedy meshed color-light textures. +pub type ColLightFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); + /// A handle to a shadow depth target. pub type ShadowDepthStencilView = gfx::handle::DepthStencilView; @@ -67,16 +72,34 @@ pub type TgtColorRes = gfx::handle::ShaderResourceView< ::View, >; +/// A handle to a greedy meshed color-light texture as a resorce. +pub type ColLightRes = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; +/// A type representing data that can be converted to an immutable texture map +/// of ColLight data (used for texture atlases created during greedy meshing). +pub type ColLightInfo = ( + Vec<<::Surface as gfx::format::SurfaceTyped>::DataType>, + Vec2, +); + /// A type that holds shadow map data. Since shadow mapping may not be /// supported on all platforms, we try to keep it separate. pub struct ShadowMapRenderer { - encoder: gfx::Encoder, + // directed_encoder: gfx::Encoder, + // point_encoder: gfx::Encoder, + directed_depth_stencil_view: ShadowDepthStencilView, + directed_res: ShadowResourceView, + directed_sampler: Sampler, - depth_stencil_view: ShadowDepthStencilView, - res: ShadowResourceView, - sampler: Sampler, + point_depth_stencil_view: ShadowDepthStencilView, + point_res: ShadowResourceView, + point_sampler: Sampler, - pipeline: GfxPipeline>, + point_pipeline: GfxPipeline>, + terrain_directed_pipeline: GfxPipeline>, + figure_directed_pipeline: GfxPipeline>, } /// A type that encapsulates rendering state. `Renderer` is central to Voxygen's @@ -114,10 +137,7 @@ pub struct Renderer { noise_tex: Texture<(gfx::format::R8, gfx::format::Unorm)>, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, - lighting_mode: LightingMode, + mode: RenderMode, } impl Renderer { @@ -128,12 +148,16 @@ impl Renderer { mut factory: gfx_backend::Factory, win_color_view: WinColorView, win_depth_view: WinDepthView, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, - lighting_mode: LightingMode, + mode: RenderMode, ) -> Result { + let dims = win_color_view.get_dimensions(); + let mut shader_reload_indicator = ReloadIndicator::new(); + let shadow_views = Self::create_shadow_views(&mut factory, (dims.0, dims.1)) + .map_err(|err| { + log::warn!("Could not create shadow map views: {:?}", err); + }) + .ok(); let ( skybox_pipeline, @@ -145,37 +169,56 @@ impl Renderer { lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, - shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, ) = create_pipelines( &mut factory, - aa_mode, - cloud_mode, - fluid_mode, - lighting_mode, + &mode, + shadow_views.is_some(), &mut shader_reload_indicator, )?; - let dims = win_color_view.get_dimensions(); let (tgt_color_view, tgt_depth_stencil_view, tgt_color_res) = - Self::create_rt_views(&mut factory, (dims.0, dims.1), aa_mode)?; + Self::create_rt_views(&mut factory, (dims.0, dims.1), &mode)?; - let shadow_map = shadow_pipeline.and_then(|pipeline| { - match Self::create_shadow_views(&mut factory, dims.0.max(dims.1)) { - Ok((depth_stencil_view, res, sampler)) => Some(ShadowMapRenderer { - encoder: factory.create_command_buffer().into(), + let shadow_map = if let ( + Some(point_pipeline), + Some(terrain_directed_pipeline), + Some(figure_directed_pipeline), + Some(shadow_views), + ) = ( + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + shadow_views, + ) { + let ( + point_depth_stencil_view, + point_res, + point_sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, + ) = shadow_views; + Some(ShadowMapRenderer { + // point_encoder: factory.create_command_buffer().into(), + // directed_encoder: factory.create_command_buffer().into(), + point_depth_stencil_view, + point_res, + point_sampler, - depth_stencil_view, - res, - sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, - pipeline, - }), - Err(err) => { - log::warn!("Could not create shadow map views: {:?}", err); - None - }, - } - }); + point_pipeline, + terrain_directed_pipeline, + figure_directed_pipeline, + }) + } else { + None + }; let sampler = factory.create_sampler_linear(); @@ -218,10 +261,7 @@ impl Renderer { noise_tex, - aa_mode, - cloud_mode, - fluid_mode, - lighting_mode, + mode, }) } @@ -253,9 +293,9 @@ impl Renderer { (&mut self.win_color_view, &mut self.win_depth_view) } - /// Change the anti-aliasing mode - pub fn set_aa_mode(&mut self, aa_mode: AaMode) -> Result<(), RenderError> { - self.aa_mode = aa_mode; + /// Change the render mode. + pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> { + self.mode = mode; // Recreate render target self.on_resize()?; @@ -266,44 +306,8 @@ impl Renderer { Ok(()) } - /// Change the cloud rendering mode - pub fn set_cloud_mode(&mut self, cloud_mode: CloudMode) -> Result<(), RenderError> { - self.cloud_mode = cloud_mode; - - // Recreate render target - self.on_resize()?; - - // Recreate pipelines with the new cloud mode - self.recreate_pipelines(); - - Ok(()) - } - - /// Change the fluid rendering mode - pub fn set_fluid_mode(&mut self, fluid_mode: FluidMode) -> Result<(), RenderError> { - self.fluid_mode = fluid_mode; - - // Recreate render target - self.on_resize()?; - - // Recreate pipelines with the new fluid mode - self.recreate_pipelines(); - - Ok(()) - } - - /// Change the lighting mode. - pub fn set_lighting_mode(&mut self, lighting_mode: LightingMode) -> Result<(), RenderError> { - self.lighting_mode = lighting_mode; - - // Recreate render target - self.on_resize()?; - - // Recreate pipelines with the new lighting mode - self.recreate_pipelines(); - - Ok(()) - } + /// Get the render mode. + pub fn render_mode(&self) -> &RenderMode { &self.mode } /// Resize internal render targets to match window render target dimensions. pub fn on_resize(&mut self) -> Result<(), RenderError> { @@ -312,10 +316,33 @@ impl Renderer { // Avoid panics when creating texture with w,h of 0,0. if dims.0 != 0 && dims.1 != 0 { let (tgt_color_view, tgt_depth_stencil_view, tgt_color_res) = - Self::create_rt_views(&mut self.factory, (dims.0, dims.1), self.aa_mode)?; + Self::create_rt_views(&mut self.factory, (dims.0, dims.1), &self.mode)?; self.tgt_color_res = tgt_color_res; self.tgt_color_view = tgt_color_view; self.tgt_depth_stencil_view = tgt_depth_stencil_view; + if let Some(shadow_map) = self.shadow_map.as_mut() { + match Self::create_shadow_views(&mut self.factory, (dims.0, dims.1)) { + Ok(( + point_depth_stencil_view, + point_res, + point_sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, + )) => { + shadow_map.point_depth_stencil_view = point_depth_stencil_view; + shadow_map.point_res = point_res; + shadow_map.point_sampler = point_sampler; + + shadow_map.directed_depth_stencil_view = directed_depth_stencil_view; + shadow_map.directed_res = directed_res; + shadow_map.directed_sampler = directed_sampler; + }, + Err(err) => { + log::warn!("Could not create shadow map views: {:?}", err); + }, + } + } } Ok(()) @@ -324,9 +351,9 @@ impl Renderer { fn create_rt_views( factory: &mut gfx_device_gl::Factory, size: (u16, u16), - aa_mode: AaMode, + mode: &RenderMode, ) -> Result<(TgtColorView, TgtDepthStencilView, TgtColorRes), RenderError> { - let kind = match aa_mode { + let kind = match mode.aa { AaMode::None | AaMode::Fxaa => { gfx::texture::Kind::D2(size.0, size.1, gfx::texture::AaMode::Single) }, @@ -379,17 +406,54 @@ impl Renderer { /// Create textures and views for shadow maps. fn create_shadow_views( factory: &mut gfx_device_gl::Factory, - size: u16, + size: (u16, u16), ) -> Result< ( ShadowDepthStencilView, ShadowResourceView, Sampler, + ShadowDepthStencilView, + ShadowResourceView, + Sampler, ), RenderError, > { + let size = Vec2::new(size.0, size.1); + let max_texture_size = Self::max_texture_size_raw(factory); let levels = 1; //10; - + let two_size = size.map(|e| { + u16::checked_next_power_of_two(e) + .filter(|&e| e <= max_texture_size) + .expect( + "Next power of two for max screen resolution axis does not fit in a texture \ + on this machine.", + ) + }); + let min_size = size.reduce_min(); + let max_size = size.reduce_max(); + let _min_two_size = two_size.reduce_min(); + let _max_two_size = two_size.reduce_max(); + // For rotated shadow maps, the maximum size of a pixel along any axis is the + // size of a diagonal along that axis. + let diag_size = size.map(f64::from).magnitude(); + let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size; + let (diag_size, _diag_cross_size) = + if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) { + // NOTE: diag_cross_size must be non-negative, since it is the ratio of a + // non-negative and a positive number (if max_size were zero, + // diag_size would be 0 too). And it must be <= diag_size, + // since min_size <= max_size. Therefore, if diag_size fits in a + // u16, so does diag_cross_size. + (diag_size as u16, diag_cross_size as u16) + } else { + panic!("Resolution of screen diagonal does not fit in a texture on this machine."); + }; + let diag_two_size = u16::checked_next_power_of_two(diag_size) + .filter(|&e| e <= max_texture_size) + .expect( + "Next power of two for resolution of screen diagonal does not fit in a texture on \ + this machine.", + ); /* let color_cty = <::Channel as gfx::format::ChannelTyped >::get_channel_type(); let tgt_color_tex = factory.create_texture( @@ -417,9 +481,12 @@ impl Renderer { let tgt_depth_stencil_view = factory.view_texture_as_depth_stencil_trivial(&tgt_depth_stencil_tex)?; */ let depth_stencil_cty = <::Channel as gfx::format::ChannelTyped>::get_channel_type(); - let shadow_tex = factory + + let point_shadow_tex = factory .create_texture( - gfx::texture::Kind::/*CubeArray*/Cube(size / 4 /* size * 2*//*, 32 */), + gfx::texture::Kind::/*CubeArray*/Cube( + /* max_two_size */ diag_two_size / 4, /* size * 2*//*, 32 */ + ), levels as gfx::texture::Level, gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, gfx::memory::Usage::Data, @@ -429,21 +496,13 @@ impl Renderer { ) .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; - let mut sampler_info = gfx::texture::SamplerInfo::new( - gfx::texture::FilterMethod::Bilinear, - gfx::texture::WrapMode::Clamp, //Border, - ); - sampler_info.comparison = Some(Comparison::Less); - // sampler_info.lod_bias = (-3.0).into(); - // sampler_info.lod_range = (1.into(), (levels - 1).into()); - sampler_info.border = [1.0; 4].into(); - let shadow_tex_sampler = factory.create_sampler(sampler_info); - let tgt_shadow_view = factory.view_texture_as_depth_stencil::( - &shadow_tex, - 0, // levels, - None, // Some(1), - gfx::texture::DepthStencilFlags::empty(), - )?; + let point_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &point_shadow_tex, + 0, // levels, + None, // Some(1), + gfx::texture::DepthStencilFlags::empty(), + )?; // let tgt_shadow_view = // factory.view_texture_as_depth_stencil_trivial(&shadow_tex)?; /* let tgt_shadow_res = factory.view_texture_as_shader_resource::( @@ -456,11 +515,61 @@ impl Renderer { // factory.view_texture_as_depth_stencil_trivial(&tgt_color_tex)?; // let tgt_shadow_view = factory.view_texture_as_shader_resource(&tgt_color_tex, // 0, None)?; - let tgt_shadow_res = factory.view_texture_as_shader_resource::( - &shadow_tex, - (0, levels - 1), - gfx::format::Swizzle::new(), - )?; + let point_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &point_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + /* println!( + "size: {:?}, two_size: {:?}, diag_size: {:?}, diag_two_size: {:?}", + size, two_size, diag_size, diag_two_size, + ); */ + let directed_shadow_tex = factory + .create_texture( + gfx::texture::Kind::D2( + /* size.x,// two_size.x,// 2 * size.x, + size.y,// two_size.y,// 2 * size.y, */ + diag_two_size, /* max_two_size*//*two_size.x */ + diag_two_size, /* max_two_size*//*two_size.y */ + /* 6*//*1, */ gfx::texture::AaMode::Single, + ), + // gfx::texture::Kind::D2Array(max_size/* * 2*/, max_size/* * 2*/, /*6*/1, + // gfx::texture::AaMode::Single), + levels as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_stencil_cty), + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + let directed_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &directed_shadow_tex, + 0, // levels, + None, // Some(1), + gfx::texture::DepthStencilFlags::empty(), + )?; + let directed_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &directed_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + let mut sampler_info = gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Border, //Clamp, + ); + sampler_info.comparison = Some(Comparison::LessEqual); + // sampler_info.lod_bias = (-3.0).into(); + // sampler_info.lod_range = (1.into(), (levels - 1).into()); + // Point lights should clamp to whatever edge value there is. + sampler_info.border = [1.0; 4].into(); + let point_shadow_tex_sampler = factory.create_sampler(sampler_info); + /* // Directed lights should always be assumed to flood areas we can't see. + sampler_info.wrap_mode = (gfx::texture::WrapMode::Border, gfx::texture::WrapMode::Border, gfx::texture::WrapMode::Border); */ + let directed_shadow_tex_sampler = factory.create_sampler(sampler_info); /* let tgt_sun_res = factory.view_texture_as_depth_stencil::( &shadow_tex, @@ -476,9 +585,12 @@ impl Renderer { )?; */ Ok(( - tgt_shadow_view, - tgt_shadow_res, - /* tgt_directed_res, */ shadow_tex_sampler, + point_tgt_shadow_view, + point_tgt_shadow_res, + point_shadow_tex_sampler, + directed_tgt_shadow_view, + directed_tgt_shadow_res, + directed_shadow_tex_sampler, )) } @@ -491,43 +603,107 @@ impl Renderer { } /// Get the resolution of the shadow render target. - pub fn get_shadow_resolution(&self) -> Vec2 { + pub fn get_shadow_resolution(&self) -> (Vec2, Vec2) { if let Some(shadow_map) = &self.shadow_map { - let dims = shadow_map.depth_stencil_view.get_dimensions(); - Vec2::new(dims.0, dims.1) + let point_dims = shadow_map.point_depth_stencil_view.get_dimensions(); + let directed_dims = shadow_map.directed_depth_stencil_view.get_dimensions(); + ( + Vec2::new(point_dims.0, point_dims.1), + Vec2::new(directed_dims.0, directed_dims.1), + ) } else { - Vec2::new(1, 1) + (Vec2::new(1, 1), Vec2::new(1, 1)) + } + } + + /// Queue the clearing of the shadow targets ready for a new frame to be + /// rendered. + pub fn clear_shadows(&mut self) { + if self.mode.shadow != ShadowMode::Map { + return; + } + if let Some(shadow_map) = self.shadow_map.as_mut() { + // let point_encoder = &mut shadow_map.point_encoder; + let point_encoder = &mut self.encoder; + point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0); + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view, 1.0); + // encoder.clear_stencil(&shadow_map.depth_stencil_view, 0); + } + } + + /// NOTE: Supported by all but a handful of mobile GPUs + /// (see https://github.com/gpuweb/gpuweb/issues/480) + /// so wgpu should support it too. + #[allow(unsafe_code)] + fn set_depth_clamp(&mut self, depth_clamp: bool) { + unsafe { + // NOTE: Currently just fail silently rather than complain if the computer is on + // a version lower than 3.3, though we probably will complain + // elsewhere regardless, since shadow mapping is an optional feature + // and having depth clamping disabled won't cause undefined + // behavior, just incorrect shadowing from objects behind the viewer. + if !self.device.get_info().is_version_supported(3, 3) { + // println!("whoops"); + return; + } + + // NOTE: Safe because glDepthClamp is (I believe) supported by + // OpenGL 3.3, so we shouldn't have to check for other OpenGL versions which + // may use different extensions. Also, enabling depth clamping should + // essentially always be safe regardless of the state of the OpenGL + // context, so no further checks are needed. + self.device.with_gl(|gl| { + // println!("gl.Enable(gfx_gl::DEPTH_CLAMP) = {:?}", + // gl.IsEnabled(gfx_gl::DEPTH_CLAMP)); + if depth_clamp { + gl.Enable(gfx_gl::DEPTH_CLAMP); + } else { + gl.Disable(gfx_gl::DEPTH_CLAMP); + } + }); } } /// Queue the clearing of the depth target ready for a new frame to be /// rendered. pub fn clear(&mut self) { - if let Some(shadow_map) = self.shadow_map.as_mut() { - let encoder = &mut shadow_map.encoder; - encoder.clear_depth(&shadow_map.depth_stencil_view, 1.0); - // encoder.clear_stencil(&shadow_map.depth_stencil_view, 0); - } self.encoder.clear_depth(&self.tgt_depth_stencil_view, 1.0); - self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0); + // self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0); self.encoder.clear_depth(&self.win_depth_view, 1.0); } + /// Set up shadow rendering. + pub fn start_shadows(&mut self) { + if self.mode.shadow != ShadowMode::Map { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + self.encoder.flush(&mut self.device); + self.set_depth_clamp(true); + } + } + /// Perform all queued draw calls for shadows. pub fn flush_shadows(&mut self) { - if let Some(shadow_map) = self.shadow_map.as_mut() { - let encoder = &mut shadow_map.encoder; - encoder.flush(&mut self.device); + if self.mode.shadow != ShadowMode::Map { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + let point_encoder = &mut self.encoder; + // let point_encoder = &mut shadow_map.point_encoder; + point_encoder.flush(&mut self.device); + // let directed_encoder = &mut shadow_map.directed_encoder; + // directed_encoder.flush(&mut self.device); + // Reset depth clamping. + self.set_depth_clamp(false); } } /// Perform all queued draw calls for this frame and clean up discarded /// items. pub fn flush(&mut self) { - if let Some(shadow_map) = self.shadow_map.as_mut() { - let encoder = &mut shadow_map.encoder; - encoder.flush(&mut self.device); - } self.encoder.flush(&mut self.device); self.device.cleanup(); @@ -541,10 +717,8 @@ impl Renderer { fn recreate_pipelines(&mut self) { match create_pipelines( &mut self.factory, - self.aa_mode, - self.cloud_mode, - self.fluid_mode, - self.lighting_mode, + &self.mode, + self.shadow_map.is_some(), &mut self.shader_reload_indicator, ) { Ok(( @@ -557,7 +731,9 @@ impl Renderer { lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, - shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, )) => { self.skybox_pipeline = skybox_pipeline; self.figure_pipeline = figure_pipeline; @@ -568,10 +744,20 @@ impl Renderer { self.lod_terrain_pipeline = lod_terrain_pipeline; self.postprocess_pipeline = postprocess_pipeline; self.player_shadow_pipeline = player_shadow_pipeline; - if let (Some(pipeline), Some(shadow_map)) = - (shadow_pipeline, self.shadow_map.as_mut()) - { - shadow_map.pipeline = pipeline; + if let ( + Some(point_pipeline), + Some(terrain_directed_pipeline), + Some(figure_directed_pipeline), + Some(shadow_map), + ) = ( + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + self.shadow_map.as_mut(), + ) { + shadow_map.point_pipeline = point_pipeline; + shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; + shadow_map.figure_directed_pipeline = figure_directed_pipeline; } }, Err(e) => error!( @@ -587,19 +773,46 @@ impl Renderer { vals: &[T], ) -> Result, RenderError> { let mut consts = Consts::new(&mut self.factory, vals.len()); - consts.update(&mut self.encoder, vals)?; + consts.update(&mut self.encoder, vals, 0)?; Ok(consts) } + /* /// Create a raw set of constants with the provided values. + pub fn create_consts_immutable( + &mut self, + vals: &[T], + ) -> Result, RenderError> { + Consts::new_immutable(&mut self.factory, vals) + } */ + /// Update a set of constants with the provided values. pub fn update_consts( &mut self, consts: &mut Consts, vals: &[T], ) -> Result<(), RenderError> { - consts.update(&mut self.encoder, vals) + consts.update(&mut self.encoder, vals, 0) } + /* /// Update a set of shadow constants with the provided values. + pub fn update_shadow_consts( + &mut self, + consts: &mut Consts, + vals: &[T], + directed_offset: usize, + point_offset: usize, + ) -> Result<(), RenderError> { + if let Some(shadow_map) = self.shadow_map.as_mut() { + let directed_encoder = &mut shadow_map.directed_encoder; + consts.update(directed_encoder, &vals[directed_offset..point_offset], directed_offset)?; + let point_encoder = &mut shadow_map.point_encoder; + consts.update(point_encoder, &vals[point_offset..], point_offset) + } else { + // Fail silently if shadows aren't working in the first place. + Ok(()) + } + } */ + /// Create a new set of instances with the provided values. pub fn create_instances( &mut self, @@ -634,7 +847,68 @@ impl Renderer { } /// Return the maximum supported texture size. - pub fn max_texture_size(&self) -> usize { self.factory.get_capabilities().max_texture_size } + pub fn max_texture_size(&self) -> u16 { Self::max_texture_size_raw(&self.factory) } + + /// Return the maximum supported texture size from the factory. + fn max_texture_size_raw(factory: &gfx_backend::Factory) -> u16 { + /// NOTE: OpenGL requirement. + const MAX_TEXTURE_SIZE_MIN: u16 = 1024; + #[cfg(target_os = "macos")] + /// NOTE: Because Macs lie about their max supported texture size. + const MAX_TEXTURE_SIZE_MAX: u16 = 8192; + #[cfg(not(target_os = "macos"))] + const MAX_TEXTURE_SIZE_MAX: u16 = u16::MAX; + // NOTE: Many APIs for textures require coordinates to fit in u16, which is why + // we perform this conversion. + u16::try_from(factory.get_capabilities().max_texture_size) + .unwrap_or(MAX_TEXTURE_SIZE_MIN) + .min(MAX_TEXTURE_SIZE_MAX) + } + + /// Create a new immutable texture from the provided image. + pub fn create_texture_immutable_raw( + &mut self, + kind: gfx::texture::Kind, + mipmap: gfx::texture::Mipmap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_immutable_raw(&mut self.factory, kind, mipmap, data, sampler_info) + } + + /// Create a new raw texture. + pub fn create_texture_raw( + &mut self, + kind: gfx::texture::Kind, + max_levels: u8, + bind: gfx::memory::Bind, + usage: gfx::memory::Usage, + levels: (u8, u8), + swizzle: gfx::format::Swizzle, + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_raw( + &mut self.device, + &mut self.factory, + kind, + max_levels, + bind, + usage, + levels, + swizzle, + sampler_info, + ) + } /// Create a new texture from the provided image. pub fn create_texture( @@ -658,6 +932,22 @@ impl Renderer { Texture::new_dynamic(&mut self.factory, dims.x, dims.y) } + /* /// Create a new greedy texture array (of color-lights). + pub fn create_greedy_texture( + &mut self, + kind: gfx::texture::Kind, + mipmap: gfx::texture::MipMap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_immutable_raw(&mut self.factory, kind, mipmap, data, sampler_info) + } */ + /// Update a texture with the provided offset, size, and data. pub fn update_texture( &mut self, @@ -741,7 +1031,7 @@ impl Renderer { map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -749,20 +1039,40 @@ impl Renderer { /// Queue the rendering of the provided figure model in the upcoming frame. pub fn render_figure( &mut self, - model: &Model, + model: &figure::FigureModel, + _col_lights: &Texture, globals: &Consts, locals: &Consts, bones: &Consts, lights: &Consts, shadows: &Consts, + light_shadows: &Consts, map: &Texture, horizon: &Texture, ) { - let shadow_maps = if let Some(shadow_map) = &mut self.shadow_map { - (shadow_map.res.clone(), shadow_map.sampler.clone()) - } else { - (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()) - }; + // return; + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let col_lights = &model.col_lights; + let model = &model.opaque; + // let atlas_model = &model.opaque; + // let model = &model.shadow; self.encoder.draw( &gfx::Slice { @@ -775,17 +1085,22 @@ impl Renderer { &self.figure_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + // abuf: atlas_model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + // col_lights: (map.srv.clone(), map.sampler.clone()), locals: locals.buf.clone(), globals: globals.buf.clone(), bones: bones.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), - shadow_maps, + light_shadows: light_shadows.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -793,20 +1108,40 @@ impl Renderer { /// Queue the rendering of the player silhouette in the upcoming frame. pub fn render_player_shadow( &mut self, - model: &Model, - globals: &Consts, - locals: &Consts, - bones: &Consts, - lights: &Consts, - shadows: &Consts, - map: &Texture, - horizon: &Texture, + _model: &figure::FigureModel, + _col_lights: &Texture, + _globals: &Consts, + _locals: &Consts, + _bones: &Consts, + _lights: &Consts, + _shadows: &Consts, + _light_shadows: &Consts, + _map: &Texture, + _horizon: &Texture, ) { - let shadow_maps = if let Some(shadow_map) = &mut self.shadow_map { - (shadow_map.res.clone(), shadow_map.sampler.clone()) - } else { - (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()) - }; + return; + /* let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let col_lights = &model.col_lights; + let model = &model.opaque; + // let atlas_model = &model.opaque; + // let model = &model.shadow; self.encoder.draw( &gfx::Slice { @@ -819,38 +1154,62 @@ impl Renderer { &self.player_shadow_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + // abuf: atlas_model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + // col_lights: (map.srv.clone(), map.sampler.clone()), locals: locals.buf.clone(), globals: globals.buf.clone(), bones: bones.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), - shadow_maps, + light_shadows: light_shadows.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (0, 0)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (0, 0) */), }, - ); + ); */ } /// Queue the rendering of the player model in the upcoming frame. pub fn render_player( &mut self, - model: &Model, + model: &figure::FigureModel, + _col_lights: &Texture, globals: &Consts, locals: &Consts, bones: &Consts, lights: &Consts, shadows: &Consts, + light_shadows: &Consts, map: &Texture, horizon: &Texture, ) { - let shadow_maps = if let Some(shadow_map) = &mut self.shadow_map { - (shadow_map.res.clone(), shadow_map.sampler.clone()) - } else { - (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()) - }; + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let col_lights = &model.col_lights; + let model = &model.opaque; + // let atlas_model = &model.opaque; + // let model = &model.shadow; self.encoder.draw( &gfx::Slice { @@ -863,17 +1222,22 @@ impl Renderer { &self.figure_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + // abuf: atlas_model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + // col_lights: (map.srv.clone(), map.sampler.clone()), locals: locals.buf.clone(), globals: globals.buf.clone(), bones: bones.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), - shadow_maps, + light_shadows: light_shadows.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -882,20 +1246,36 @@ impl Renderer { /// frame. pub fn render_terrain_chunk( &mut self, + // atlas_model: &Model, // model: &Model, model: &Model, + col_lights: &Texture, globals: &Consts, locals: &Consts, lights: &Consts, shadows: &Consts, + light_shadows: &Consts, map: &Texture, horizon: &Texture, ) { - let shadow_maps = if let Some(shadow_map) = &mut self.shadow_map { - (shadow_map.res.clone(), shadow_map.sampler.clone()) - } else { - (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()) - }; + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; self.encoder.draw( &gfx::Slice { @@ -908,40 +1288,54 @@ impl Renderer { &self.terrain_pipeline.pso, &terrain::pipe::Data { vbuf: model.vbuf.clone(), + // TODO: Consider splitting out texture atlas data into a separate vertex buffer, + // since we don't need it for things like shadows. + // abuf: atlas_model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + // col_lights: (map.srv.clone(), map.sampler.clone()), locals: locals.buf.clone(), globals: globals.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), - shadow_maps, + light_shadows: light_shadows.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } - /// Queue the rendering of the player silhouette in the upcoming frame. - pub fn render_shadow( + /// Queue the rendering of a shadow map from a point light in the upcoming + /// frame. + pub fn render_shadow_point( &mut self, - model: &Model, + model: &Model, + // model: &Model, globals: &Consts, terrain_locals: &Consts, locals: &Consts, - lights: &Consts, - shadows: &Consts, - map: &Texture, - horizon: &Texture, + /* lights: &Consts, + * shadows: &Consts, + * map: &Texture, + * horizon: &Texture, */ ) { + if self.mode.shadow != ShadowMode::Map { + return; + } // NOTE: Don't render shadows if the shader is not supported. let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { shadow_map } else { return; }; - let encoder = &mut shadow_map.encoder; - encoder.draw( + + // let point_encoder = &mut shadow_map.point_encoder; + let point_encoder = &mut self.encoder; + point_encoder.draw( &gfx::Slice { start: model.vertex_range().start, end: model.vertex_range().end, @@ -949,21 +1343,134 @@ impl Renderer { instances: None, buffer: gfx::IndexBuffer::Auto, }, - &shadow_map.pipeline.pso, + &shadow_map.point_pipeline.pso, &shadow::pipe::Data { // Terrain vertex stuff vbuf: model.vbuf.clone(), locals: terrain_locals.buf.clone(), globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), - noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), - map: (map.srv.clone(), map.sampler.clone()), - horizon: (horizon.srv.clone(), horizon.sampler.clone()), + // lights: lights.buf.clone(), + // shadows: shadows.buf.clone(), + // noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + // map: (map.srv.clone(), map.sampler.clone()), + // horizon: (horizon.srv.clone(), horizon.sampler.clone()), // Shadow stuff light_shadows: locals.buf.clone(), - tgt_depth_stencil: shadow_map.depth_stencil_view.clone(), + tgt_depth_stencil: shadow_map.point_depth_stencil_view.clone(), + /* tgt_depth_stencil: (self.shadow_depth_stencil_view.clone(), (1, 1)), + * shadow_tex: (self.shadow_res.clone(), self.shadow_sampler.clone()), */ + }, + ); + } + + /// Queue the rendering of terrain shadow map from all directional lights in + /// the upcoming frame. + pub fn render_terrain_shadow_directed( + &mut self, + // model: &Model, + model: &Model, + globals: &Consts, + terrain_locals: &Consts, + locals: &Consts, + /* lights: &Consts, + * shadows: &Consts, + * map: &Texture, + * horizon: &Texture, */ + ) { + if self.mode.shadow != ShadowMode::Map { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.terrain_directed_pipeline.pso, + &shadow::pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: terrain_locals.buf.clone(), + globals: globals.buf.clone(), + // lights: lights.buf.clone(), + // shadows: shadows.buf.clone(), + // noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + // map: (map.srv.clone(), map.sampler.clone()), + // horizon: (horizon.srv.clone(), horizon.sampler.clone()), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), + /* tgt_depth_stencil: (self.shadow_depth_stencil_view.clone(), (1, 1)), + * shadow_tex: (self.shadow_res.clone(), self.shadow_sampler.clone()), */ + }, + ); + } + + /// Queue the rendering of figure shadow map from all directional lights in + /// the upcoming frame. + pub fn render_figure_shadow_directed( + &mut self, + // model: &Model, + model: &figure::FigureModel, + globals: &Consts, + figure_locals: &Consts, + bones: &Consts, + locals: &Consts, + /* lights: &Consts, + * shadows: &Consts, + * map: &Texture, + * horizon: &Texture, */ + ) { + if self.mode.shadow != ShadowMode::Map { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + let model = &model.opaque; + + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.figure_directed_pipeline.pso, + &shadow::figure_pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: figure_locals.buf.clone(), + bones: bones.buf.clone(), + globals: globals.buf.clone(), + // lights: lights.buf.clone(), + // shadows: shadows.buf.clone(), + // noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + // map: (map.srv.clone(), map.sampler.clone()), + // horizon: (horizon.srv.clone(), horizon.sampler.clone()), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), /* tgt_depth_stencil: (self.shadow_depth_stencil_view.clone(), (1, 1)), * shadow_tex: (self.shadow_res.clone(), self.shadow_sampler.clone()), */ }, @@ -979,15 +1486,30 @@ impl Renderer { locals: &Consts, lights: &Consts, shadows: &Consts, + light_shadows: &Consts, map: &Texture, horizon: &Texture, waves: &Texture, ) { - let shadow_maps = if let Some(shadow_map) = &mut self.shadow_map { - (shadow_map.res.clone(), shadow_map.sampler.clone()) - } else { - (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()) - }; + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -1003,13 +1525,15 @@ impl Renderer { globals: globals.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), - shadow_maps, + light_shadows: light_shadows.buf.clone(), + point_shadow_maps, + directed_shadow_maps, map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), waves: (waves.srv.clone(), waves.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -1019,39 +1543,65 @@ impl Renderer { pub fn render_sprites( &mut self, model: &Model, + col_lights: &Texture, globals: &Consts, + terrain_locals: &Consts, + locals: &Consts, + // instance_count: usize, + // instances: &Consts, instances: &Instances, lights: &Consts, shadows: &Consts, + light_shadows: &Consts, map: &Texture, horizon: &Texture, ) { - let shadow_maps = if let Some(shadow_map) = &mut self.shadow_map { - (shadow_map.res.clone(), shadow_map.sampler.clone()) - } else { - (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()) - }; + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, end: model.vertex_range().end, base_vertex: 0, - instances: Some((instances.count() as u32, 0)), + instances: Some((instances.count()/*instance_count*/ as u32, 0)), buffer: gfx::IndexBuffer::Auto, }, &self.sprite_pipeline.pso, &sprite::pipe::Data { vbuf: model.vbuf.clone(), ibuf: instances.ibuf.clone(), + // ibuf: instances.buf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + terrain_locals: terrain_locals.buf.clone(), + locals: locals.buf.clone(), globals: globals.buf.clone(), lights: lights.buf.clone(), shadows: shadows.buf.clone(), - shadow_maps, + light_shadows: light_shadows.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -1083,7 +1633,7 @@ impl Renderer { map: (map.srv.clone(), map.sampler.clone()), horizon: (horizon.srv.clone(), horizon.sampler.clone()), tgt_color: self.tgt_color_view.clone(), - tgt_depth_stencil: (self.tgt_depth_stencil_view.clone(), (1, 1)), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -1162,10 +1712,8 @@ struct GfxPipeline { /// Creates all the pipelines used to render. fn create_pipelines( factory: &mut gfx_backend::Factory, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, - lighting_mode: LightingMode, + mode: &RenderMode, + has_shadow_views: bool, shader_reload_indicator: &mut ReloadIndicator, ) -> Result< ( @@ -1179,6 +1727,8 @@ fn create_pipelines( GfxPipeline>, GfxPipeline>, Option>>, + Option>>, + Option>>, ), RenderError, > { @@ -1205,6 +1755,9 @@ fn create_pipelines( let lod = assets::load_watched::("voxygen.shaders.include.lod", shader_reload_indicator) .unwrap(); + let shadows = + assets::load_watched::("voxygen.shaders.include.shadows", shader_reload_indicator) + .unwrap(); // We dynamically add extra configuration settings to the constants file. let constants = format!( @@ -1215,28 +1768,34 @@ fn create_pipelines( #define FLUID_MODE {} #define CLOUD_MODE {} #define LIGHTING_ALGORITHM {} +#define SHADOW_MODE {} "#, constants, // TODO: Configurable vertex/fragment shader preference. "VOXYGEN_COMPUTATION_PREERENCE_FRAGMENT", - match fluid_mode { + match mode.fluid { FluidMode::Cheap => "FLUID_MODE_CHEAP", FluidMode::Shiny => "FLUID_MODE_SHINY", }, - match cloud_mode { + match mode.cloud { CloudMode::None => "CLOUD_MODE_NONE", CloudMode::Regular => "CLOUD_MODE_REGULAR", }, - match lighting_mode { + match mode.lighting { LightingMode::Ashikmin => "LIGHTING_ALGORITHM_ASHIKHMIN", LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG", LightingMode::Lambertian => "CLOUD_MODE_NONE", }, + 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 = assets::load_watched::( - &["voxygen.shaders.antialias.", match aa_mode { + &["voxygen.shaders.antialias.", match mode.aa { AaMode::None | AaMode::SsaaX4 => "none", AaMode::Fxaa => "fxaa", AaMode::MsaaX4 => "msaa-x4", @@ -1249,7 +1808,7 @@ fn create_pipelines( .unwrap(); let cloud = assets::load_watched::( - &["voxygen.shaders.include.cloud.", match cloud_mode { + &["voxygen.shaders.include.cloud.", match mode.cloud { CloudMode::None => "none", CloudMode::Regular => "regular", }] @@ -1261,6 +1820,7 @@ fn create_pipelines( let mut include_ctx = IncludeContext::new(); include_ctx.include("constants.glsl", &constants); include_ctx.include("globals.glsl", &globals); + include_ctx.include("shadows.glsl", &shadows); include_ctx.include("sky.glsl", &sky); include_ctx.include("light.glsl", &light); include_ctx.include("srgb.glsl", &srgb); @@ -1269,6 +1829,41 @@ fn create_pipelines( include_ctx.include("anti-aliasing.glsl", &anti_alias); include_ctx.include("cloud.glsl", &cloud); + let figure_vert = + assets::load_watched::("voxygen.shaders.figure-vert", shader_reload_indicator) + .unwrap(); + + let terrain_point_shadow_vert = assets::load_watched::( + "voxygen.shaders.light-shadows-vert", + shader_reload_indicator, + ) + .unwrap(); + + let terrain_directed_shadow_vert = assets::load_watched::( + "voxygen.shaders.light-shadows-directed-vert", + shader_reload_indicator, + ) + .unwrap(); + + let figure_directed_shadow_vert = assets::load_watched::( + "voxygen.shaders.light-shadows-figure-vert", + shader_reload_indicator, + ) + .unwrap(); + + /* let directed_shadow_geom = + &assets::load_watched::( + "voxygen.shaders.light-shadows-directed-geom", + shader_reload_indicator, + ) + .unwrap(); */ + + let directed_shadow_frag = &assets::load_watched::( + "voxygen.shaders.light-shadows-directed-frag", + shader_reload_indicator, + ) + .unwrap(); + // Construct a pipeline for rendering skyboxes let skybox_pipeline = create_pipeline( factory, @@ -1285,8 +1880,7 @@ fn create_pipelines( let figure_pipeline = create_pipeline( factory, figure::pipe::new(), - &assets::load_watched::("voxygen.shaders.figure-vert", shader_reload_indicator) - .unwrap(), + &figure_vert, &assets::load_watched::("voxygen.shaders.figure-frag", shader_reload_indicator) .unwrap(), &include_ctx, @@ -1312,7 +1906,7 @@ fn create_pipelines( &assets::load_watched::("voxygen.shaders.fluid-vert", shader_reload_indicator) .unwrap(), &assets::load_watched::( - &["voxygen.shaders.fluid-frag.", match fluid_mode { + &["voxygen.shaders.fluid-frag.", match mode.fluid { FluidMode::Cheap => "cheap", FluidMode::Shiny => "shiny", }] @@ -1388,18 +1982,15 @@ fn create_pipelines( let player_shadow_pipeline = create_pipeline( factory, figure::pipe::Init { - tgt_depth_stencil: ( - gfx::preset::depth::PASS_TEST, - Stencil::new( - Comparison::Equal, - 0xff, - (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep), - ), - ), + tgt_depth_stencil: (gfx::preset::depth::PASS_TEST/*, + Stencil::new( + Comparison::Equal, + 0xff, + (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep), + ),*/), ..figure::pipe::new() }, - &assets::load_watched::("voxygen.shaders.figure-vert", shader_reload_indicator) - .unwrap(), + &figure_vert, &assets::load_watched::( "voxygen.shaders.player-shadow-frag", shader_reload_indicator, @@ -1409,20 +2000,18 @@ fn create_pipelines( gfx::state::CullFace::Back, )?; - // Construct a pipeline for rendering shadow maps. - let shadow_pipeline = match create_shadow_pipeline( + // Construct a pipeline for rendering point light terrain shadow maps. + let point_shadow_pipeline = match create_shadow_pipeline( factory, shadow::pipe::new(), - &assets::load_watched::( - "voxygen.shaders.light-shadows-vert", - shader_reload_indicator, - ) - .unwrap(), - &assets::load_watched::( - "voxygen.shaders.light-shadows-geom", - shader_reload_indicator, - ) - .unwrap(), + &terrain_point_shadow_vert, + Some( + &assets::load_watched::( + "voxygen.shaders.light-shadows-geom", + shader_reload_indicator, + ) + .unwrap(), + ), &assets::load_watched::( "voxygen.shaders.light-shadows-frag", shader_reload_indicator, @@ -1430,10 +2019,57 @@ fn create_pipelines( .unwrap(), &include_ctx, gfx::state::CullFace::Back, + None, //Some(gfx::state::Offset(2, 10)), ) { Ok(pipe) => Some(pipe), Err(err) => { - log::warn!("Could not load shadow map pipeline: {:?}", err); + log::warn!("Could not load point shadow map pipeline: {:?}", err); + None + }, + }; + + // Construct a pipeline for rendering directional light terrain shadow maps. + let terrain_directed_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::pipe::new(), + &terrain_directed_shadow_vert, + None, // &directed_shadow_geom, + &directed_shadow_frag, + &include_ctx, + gfx::state::CullFace::Back, + None, + /* Some(gfx::state::Offset(4, 10)), + * Some(gfx::state::Offset(2, 10)), */ + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + log::warn!( + "Could not load directed terrain shadow map pipeline: {:?}", + err + ); + None + }, + }; + + // Construct a pipeline for rendering directional light figure shadow maps. + let figure_directed_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::figure_pipe::new(), + &figure_directed_shadow_vert, + None, // &directed_shadow_geom, + &directed_shadow_frag, + &include_ctx, + gfx::state::CullFace::Back, + None, + /* Some(gfx::state::Offset(4, 10)), + * Some(gfx::state::Offset(2, 10)), */ + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + log::warn!( + "Could not load directed figure shadow map pipeline: {:?}", + err + ); None }, }; @@ -1448,7 +2084,9 @@ fn create_pipelines( lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, - shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, )) } @@ -1489,17 +2127,21 @@ fn create_shadow_pipeline( factory: &mut gfx_backend::Factory, pipe: P, vs: &str, - gs: &str, + gs: Option<&str>, fs: &str, ctx: &IncludeContext, cull_face: gfx::state::CullFace, + offset: Option, ) -> Result, RenderError> { let vs = ctx.expand(vs)?; - let gs = ctx.expand(gs)?; + let gs = gs.map(|gs| ctx.expand(gs)).transpose()?; let fs = ctx.expand(fs)?; - let shader_set = - factory.create_shader_set_geometry(vs.as_bytes(), gs.as_bytes(), fs.as_bytes())?; + let shader_set = if let Some(gs) = gs { + factory.create_shader_set_geometry(vs.as_bytes(), gs.as_bytes(), fs.as_bytes())? + } else { + factory.create_shader_set(vs.as_bytes(), fs.as_bytes())? + }; let result = Ok(GfxPipeline { pso: factory.create_pipeline_state( @@ -1516,7 +2158,7 @@ fn create_shadow_pipeline( gfx::state::CullFace::Nothing => gfx::state::CullFace::Nothing, }, method: gfx::state::RasterMethod::Fill, - offset: None, //Some(gfx::state::Offset(2, 10)), + offset, samples: None, // Some(gfx::state::MultiSample), }, pipe, diff --git a/voxygen/src/render/texture.rs b/voxygen/src/render/texture.rs index 3f83ca37c6..a41953d135 100644 --- a/voxygen/src/render/texture.rs +++ b/voxygen/src/render/texture.rs @@ -89,6 +89,58 @@ where }) } + pub fn new_immutable_raw( + factory: &mut gfx_backend::Factory, + kind: gfx::texture::Kind, + mipmap: gfx::texture::Mipmap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result { + let (tex, srv) = factory + .create_texture_immutable::(kind, mipmap, data) + .map_err(|err| RenderError::CombinedError(err))?; + + Ok(Self { + tex, + srv, + sampler: factory.create_sampler(sampler_info), + }) + } + + pub fn new_raw( + _device: &mut gfx_backend::Device, + factory: &mut gfx_backend::Factory, + kind: gfx::texture::Kind, + max_levels: u8, + bind: gfx::memory::Bind, + usage: gfx::memory::Usage, + levels: (u8, u8), + swizzle: gfx::format::Swizzle, + sampler_info: gfx::texture::SamplerInfo, + ) -> Result { + let tex = factory + .create_texture( + kind, + max_levels as gfx::texture::Level, + bind | gfx::memory::Bind::SHADER_RESOURCE, + usage, + Some(<::Channel as gfx::format::ChannelTyped>::get_channel_type()) + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + + // device.cleanup(); + + let srv = factory + .view_texture_as_shader_resource::(&tex, levels, swizzle) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?; + + Ok(Self { + tex, + srv, + sampler: factory.create_sampler(sampler_info), + }) + } + /// Update a texture with the given data (used for updating the glyph cache /// texture). pub fn update( diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index 40fcab769f..5637e64e8e 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -3,8 +3,8 @@ use std::f32::consts::PI; use treeculler::Frustum; use vek::*; -const NEAR_PLANE: f32 = 0.5; -const FAR_PLANE: f32 = 100000.0; +pub const NEAR_PLANE: f32 = 0.5; +pub const FAR_PLANE: f32 = 100000.0; const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; @@ -43,6 +43,7 @@ pub struct Camera { last_time: Option, dependents: Dependents, + frustum: Frustum, } impl Camera { @@ -66,6 +67,7 @@ impl Camera { proj_mat: Mat4::identity(), cam_pos: Vec3::zero(), }, + frustum: Frustum::from_modelview_projection(Mat4::identity().into_col_arrays()), } } @@ -95,6 +97,7 @@ impl Camera { (_, Err(_)) => self.dist, } .max(0.0) + // .max(NEAR_PLANE) // self.dist.max(0.0) }; @@ -104,20 +107,22 @@ impl Camera { * Mat4::rotation_x(self.ori.y) * Mat4::rotation_y(self.ori.x) * Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x()) - * Mat4::translation_3d(-self.focus); + * Mat4::translation_3d(-self.focus.map(|e| e.fract())); self.dependents.proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE); // TODO: Make this more efficient. self.dependents.cam_pos = Vec3::from(self.dependents.view_mat.inverted() * Vec4::unit_w()); + self.frustum = Frustum::from_modelview_projection( + (self.dependents.proj_mat + * self.dependents.view_mat + * Mat4::translation_3d(-self.focus.map(|e| e.trunc()))) + .into_col_arrays(), + ); } - pub fn frustum(&self) -> Frustum { - Frustum::from_modelview_projection( - (self.dependents.proj_mat * self.dependents.view_mat).into_col_arrays(), - ) - } + pub fn frustum(&self) -> &Frustum { &self.frustum } pub fn dependents(&self) -> Dependents { self.dependents.clone() } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index c1628ffab0..f9a19e902a 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -1,8 +1,8 @@ use super::load::*; use crate::{ anim::{self, Skeleton}, - mesh::Meshable, - render::{FigurePipeline, Mesh, Model, Renderer}, + mesh::{greedy::GreedyMesh, Meshable}, + render::{BoneMeshes, FigureModel, FigurePipeline, Mesh, Renderer}, scene::camera::CameraMode, }; use common::{ @@ -21,6 +21,8 @@ use std::{ }; use vek::*; +pub type FigureModelEntry = [FigureModel; 3]; + #[derive(PartialEq, Eq, Hash, Clone)] enum FigureKey { Simple(Body), @@ -68,7 +70,7 @@ pub struct FigureModelCache where Skel: Skeleton, { - models: HashMap; 3], Skel::Attr), u64)>, + models: HashMap, manifest_indicator: ReloadIndicator, } @@ -86,8 +88,8 @@ impl FigureModelCache { character_state: Option<&CharacterState>, camera_mode: CameraMode, manifest_indicator: &mut ReloadIndicator, - generate_mesh: fn(&Segment, Vec3) -> Mesh, - ) -> [Option>; 16] { + mut generate_mesh: impl FnMut(Segment, Vec3) -> BoneMeshes, + ) -> [Option; 16] { match body { Body::Humanoid(body) => { let humanoid_head_spec = HumHeadSpec::load_watched(manifest_indicator); @@ -119,7 +121,7 @@ impl FigureModelCache { body.skin, body.eyebrows, body.accessory, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), CameraMode::FirstPerson => None, }, @@ -127,27 +129,31 @@ impl FigureModelCache { CameraMode::ThirdPerson => Some(humanoid_armor_chest_spec.mesh_chest( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), CameraMode::FirstPerson => None, }, match camera_mode { - CameraMode::ThirdPerson => { - Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout, generate_mesh)) - }, + CameraMode::ThirdPerson => Some(humanoid_armor_belt_spec.mesh_belt( + &body, + loadout, + |segment, offset| generate_mesh(segment, offset), + )), CameraMode::FirstPerson => None, }, match camera_mode { - CameraMode::ThirdPerson => { - Some(humanoid_armor_back_spec.mesh_back(&body, loadout, generate_mesh)) - }, + CameraMode::ThirdPerson => Some(humanoid_armor_back_spec.mesh_back( + &body, + loadout, + |segment, offset| generate_mesh(segment, offset), + )), CameraMode::FirstPerson => None, }, match camera_mode { CameraMode::ThirdPerson => Some(humanoid_armor_pants_spec.mesh_pants( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), CameraMode::FirstPerson => None, }, @@ -156,7 +162,11 @@ impl FigureModelCache { { None } else { - Some(humanoid_armor_hand_spec.mesh_left_hand(&body, loadout, generate_mesh)) + Some(humanoid_armor_hand_spec.mesh_left_hand( + &body, + loadout, + |segment, offset| generate_mesh(segment, offset), + )) }, if character_state.map(|cs| cs.is_dodge()).unwrap_or_default() { None @@ -164,14 +174,14 @@ impl FigureModelCache { Some(humanoid_armor_hand_spec.mesh_right_hand( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )) }, match camera_mode { CameraMode::ThirdPerson => Some(humanoid_armor_foot_spec.mesh_left_foot( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), CameraMode::FirstPerson => None, }, @@ -179,7 +189,7 @@ impl FigureModelCache { CameraMode::ThirdPerson => Some(humanoid_armor_foot_spec.mesh_right_foot( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), CameraMode::FirstPerson => None, }, @@ -188,7 +198,7 @@ impl FigureModelCache { Some(humanoid_armor_shoulder_spec.mesh_left_shoulder( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )) }, CameraMode::FirstPerson => None, @@ -198,12 +208,14 @@ impl FigureModelCache { Some(humanoid_armor_shoulder_spec.mesh_right_shoulder( &body, loadout, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )) }, CameraMode::FirstPerson => None, }, - Some(mesh_glider(generate_mesh)), + Some(mesh_glider(|segment, offset| { + generate_mesh(segment, offset) + })), if camera_mode != CameraMode::FirstPerson || character_state .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) @@ -211,7 +223,7 @@ impl FigureModelCache { { Some(humanoid_main_weapon_spec.mesh_main_weapon( loadout.active_item.as_ref().map(|i| &i.item.kind), - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )) } else { None @@ -231,32 +243,32 @@ impl FigureModelCache { Some(quadruped_small_central_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_central_spec.mesh_chest( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_lf( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_rf( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_lb( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_rb( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -280,57 +292,57 @@ impl FigureModelCache { Some(quadruped_medium_central_spec.mesh_head_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_head_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_jaw( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_torso_f( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_torso_b( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_ears( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_lf( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_rf( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_lb( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_rb( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -349,37 +361,37 @@ impl FigureModelCache { Some(bird_medium_center_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_center_spec.mesh_torso( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_center_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_wing_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_wing_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_foot_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(bird_medium_lateral_spec.mesh_foot_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -393,12 +405,24 @@ impl FigureModelCache { ] }, Body::FishMedium(body) => [ - Some(mesh_fish_medium_head(body.head, generate_mesh)), - Some(mesh_fish_medium_torso(body.torso, generate_mesh)), - Some(mesh_fish_medium_rear(body.rear, generate_mesh)), - Some(mesh_fish_medium_tail(body.tail, generate_mesh)), - Some(mesh_fish_medium_fin_l(body.fin_l, generate_mesh)), - Some(mesh_fish_medium_fin_r(body.fin_r, generate_mesh)), + Some(mesh_fish_medium_head(body.head, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_torso(body.torso, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_rear(body.rear, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_tail(body.tail, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_fin_l(body.fin_l, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_medium_fin_r(body.fin_r, |segment, offset| { + generate_mesh(segment, offset) + })), None, None, None, @@ -418,82 +442,94 @@ impl FigureModelCache { Some(dragon_center_spec.mesh_head_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_head_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), + )), + Some(dragon_center_spec.mesh_jaw( + body.species, + body.body_type, + |segment, offset| generate_mesh(segment, offset), )), - Some(dragon_center_spec.mesh_jaw(body.species, body.body_type, generate_mesh)), Some(dragon_center_spec.mesh_chest_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_chest_rear( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_tail_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_center_spec.mesh_tail_rear( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_in_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_in_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_out_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_wing_out_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(dragon_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, ] }, Body::BirdSmall(body) => [ - Some(mesh_bird_small_head(body.head, generate_mesh)), - Some(mesh_bird_small_torso(body.torso, generate_mesh)), - Some(mesh_bird_small_wing_l(body.wing_l, generate_mesh)), - Some(mesh_bird_small_wing_r(body.wing_r, generate_mesh)), + Some(mesh_bird_small_head(body.head, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_bird_small_torso(body.torso, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_bird_small_wing_l(body.wing_l, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_bird_small_wing_r(body.wing_r, |segment, offset| { + generate_mesh(segment, offset) + })), None, None, None, @@ -508,8 +544,12 @@ impl FigureModelCache { None, ], Body::FishSmall(body) => [ - Some(mesh_fish_small_torso(body.torso, generate_mesh)), - Some(mesh_fish_small_tail(body.tail, generate_mesh)), + Some(mesh_fish_small_torso(body.torso, |segment, offset| { + generate_mesh(segment, offset) + })), + Some(mesh_fish_small_tail(body.tail, |segment, offset| { + generate_mesh(segment, offset) + })), None, None, None, @@ -535,57 +575,57 @@ impl FigureModelCache { Some(biped_large_center_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_torso_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_torso_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_shoulder_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_shoulder_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_hand_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_hand_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_leg_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_leg_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_foot_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_lateral_spec.mesh_foot_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -599,51 +639,55 @@ impl FigureModelCache { let golem_lateral_spec = GolemLateralSpec::load_watched(manifest_indicator); [ - Some(golem_center_spec.mesh_head(body.species, body.body_type, generate_mesh)), + Some(golem_center_spec.mesh_head( + body.species, + body.body_type, + |segment, offset| generate_mesh(segment, offset), + )), Some(golem_center_spec.mesh_torso_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_shoulder_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_shoulder_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_hand_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_hand_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_leg_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_leg_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_foot_l( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(golem_lateral_spec.mesh_foot_r( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -660,27 +704,27 @@ impl FigureModelCache { Some(critter_center_spec.mesh_head( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_chest( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_feet_f( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_feet_b( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(critter_center_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -719,12 +763,13 @@ impl FigureModelCache { pub fn get_or_create_model( &mut self, renderer: &mut Renderer, + col_lights: &mut super::FigureColLights, body: Body, loadout: Option<&Loadout>, tick: u64, camera_mode: CameraMode, character_state: Option<&CharacterState>, - ) -> &([Model; 3], Skel::Attr) + ) -> &(FigureModelEntry, Skel::Attr) where for<'a> &'a common::comp::Body: std::convert::TryInto, Skel::Attr: Default, @@ -754,58 +799,72 @@ impl FigureModelCache { .unwrap_or_else(::default); let manifest_indicator = &mut self.manifest_indicator; - let mut make_model = |generate_mesh| { - let mut mesh = Mesh::new(); + let mut make_model = |generate_mesh: for<'a> fn(&mut GreedyMesh<'a>, _, _) -> _| { + let mut greedy = FigureModel::make_greedy(); + let mut opaque = Mesh::new(); + let mut figure_bounds = Aabb { + min: Vec3::zero(), + max: Vec3::zero(), + }; + // let mut shadow = Mesh::new(); Self::bone_meshes( body, loadout, character_state, camera_mode, manifest_indicator, - generate_mesh, + |segment, offset| generate_mesh(&mut greedy, segment, offset), ) .iter() + // .zip(&mut figure_bounds) .enumerate() .filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm))) - .for_each(|(i, bone_mesh)| { - mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8)) + .for_each(|(i, (opaque_mesh/*, shadow_mesh*/, bounds)/*, bone_bounds*/)| { + // opaque.push_mesh_map(opaque_mesh, |vert| vert.with_bone_idx(i as u8)); + opaque.push_mesh_map(opaque_mesh, |vert| vert.with_bone_idx(i as u8)); + figure_bounds.expand_to_contain(*bounds); + // shadow.push_mesh_map(shadow_mesh, |vert| vert.with_bone_idx(i as u8)); }); - renderer.create_model(&mesh).unwrap() + col_lights.create_figure(renderer, greedy, (opaque/*, shadow*/, figure_bounds)).unwrap() }; - fn generate_mesh( - segment: &Segment, + fn generate_mesh<'a>( + greedy: &mut GreedyMesh<'a>, + segment: Segment, offset: Vec3, - ) -> Mesh { - Meshable::::generate_mesh( + ) -> BoneMeshes { + let (opaque, _, /*shadow*/_, bounds) = Meshable::::generate_mesh( segment, - (offset, Vec3::one()), - ) - .0 + (greedy, offset, Vec3::one()), + ); + (opaque/*, shadow*/, bounds) } - fn generate_mesh_lod_mid( - segment: &Segment, + fn generate_mesh_lod_mid<'a>( + greedy: &mut GreedyMesh<'a>, + segment: Segment, offset: Vec3, - ) -> Mesh { + ) -> BoneMeshes { let lod_scale = Vec3::broadcast(0.6); - Meshable::::generate_mesh( - &segment.scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0 + let (opaque, _, /*shadow*/_, bounds) = Meshable::::generate_mesh( + segment.scaled_by(lod_scale), + (greedy, offset * lod_scale, Vec3::one() / lod_scale), + ); + (opaque/*, shadow*/, bounds) } - fn generate_mesh_lod_low( - segment: &Segment, + fn generate_mesh_lod_low<'a>( + greedy: &mut GreedyMesh<'a>, + segment: Segment, offset: Vec3, - ) -> Mesh { + ) -> BoneMeshes { let lod_scale = Vec3::broadcast(0.3); - Meshable::::generate_mesh( - &segment.scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0 + let segment = segment.scaled_by(lod_scale); + let (opaque, _, /*shadow*/_, bounds) = Meshable::::generate_mesh( + segment, + (greedy, offset * lod_scale, Vec3::one() / lod_scale), + ); + (opaque/*, shadow*/, bounds) } ( @@ -824,14 +883,24 @@ impl FigureModelCache { } } - pub fn clean(&mut self, tick: u64) { + pub fn clean(&mut self, col_lights: &mut super::FigureColLights, tick: u64) { // Check for reloaded manifests // TODO: maybe do this in a different function, maintain? if self.manifest_indicator.reloaded() { + col_lights.atlas.clear(); self.models.clear(); } // TODO: Don't hard-code this. - self.models - .retain(|_, (_, last_used)| *last_used + 60 > tick); + if tick % 60 == 0 { + self.models.retain(|_, ((models, _), last_used)| { + let alive = *last_used + 60 > tick; + if !alive { + models.iter().for_each(|model| { + col_lights.atlas.deallocate(model.allocation.id); + }); + } + alive + }); + } } } diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index e8960bdf54..afa75e7b99 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -1,4 +1,4 @@ -use crate::render::{FigurePipeline, Mesh}; +use crate::render::{BoneMeshes, Mesh}; use common::{ assets::{self, watch::ReloadIndicator, Asset}, comp::{ @@ -56,9 +56,9 @@ fn graceful_load_mat_segment_flipped(mesh_name: &str) -> MatSegment { pub fn load_mesh( mesh_name: &str, position: Vec3, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { - generate_mesh(&load_segment(mesh_name), position) + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { + generate_mesh(load_segment(mesh_name), position) } fn color_segment( @@ -159,8 +159,8 @@ impl HumHeadSpec { skin: u8, _eyebrows: u8, accessory: u8, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(race, body_type)) { Some(spec) => spec, None => { @@ -227,7 +227,7 @@ impl HumHeadSpec { .unify(); generate_mesh( - &head, + head, Vec3::from(spec.offset) + origin_offset.map(|e| e as f32 * -1.0), ) } @@ -355,8 +355,8 @@ impl HumArmorShoulderSpec { body: &Body, loadout: &Loadout, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Shoulder(shoulder), .. @@ -405,15 +405,15 @@ impl HumArmorShoulderSpec { shoulder_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(shoulder_color))); } - generate_mesh(&shoulder_segment, Vec3::from(offset)) + generate_mesh(shoulder_segment, Vec3::from(offset)) } pub fn mesh_left_shoulder( &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { self.mesh_shoulder(body, loadout, true, generate_mesh) } @@ -421,8 +421,8 @@ impl HumArmorShoulderSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { self.mesh_shoulder(body, loadout, false, generate_mesh) } } @@ -437,8 +437,8 @@ impl HumArmorChestSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Chest(chest), .. @@ -479,7 +479,7 @@ impl HumArmorChestSpec { .unify() .0; - generate_mesh(&chest, Vec3::from(spec.vox_spec.1)) + generate_mesh(chest, Vec3::from(spec.vox_spec.1)) } } // Hand @@ -494,8 +494,8 @@ impl HumArmorHandSpec { body: &Body, loadout: &Loadout, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Hand(hand), .. @@ -538,15 +538,15 @@ impl HumArmorHandSpec { hand_segment = hand_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(hand_color))); } - generate_mesh(&hand_segment, Vec3::from(offset)) + generate_mesh(hand_segment, Vec3::from(offset)) } pub fn mesh_left_hand( &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { self.mesh_hand(body, loadout, true, generate_mesh) } @@ -554,8 +554,8 @@ impl HumArmorHandSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { self.mesh_hand(body, loadout, false, generate_mesh) } } @@ -570,8 +570,8 @@ impl HumArmorBeltSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Belt(belt), .. @@ -600,7 +600,7 @@ impl HumArmorBeltSpec { belt_segment = belt_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(belt_color))); } - generate_mesh(&belt_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(belt_segment, Vec3::from(spec.vox_spec.1)) } } // Cape @@ -614,8 +614,8 @@ impl HumArmorBackSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Back(back), .. @@ -643,7 +643,7 @@ impl HumArmorBackSpec { back_segment = back_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(back_color))); } - generate_mesh(&back_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(back_segment, Vec3::from(spec.vox_spec.1)) } } // Legs @@ -657,8 +657,8 @@ impl HumArmorPantsSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Pants(pants), .. @@ -699,7 +699,7 @@ impl HumArmorPantsSpec { .unify() .0; - generate_mesh(&pants, Vec3::from(spec.vox_spec.1)) + generate_mesh(pants, Vec3::from(spec.vox_spec.1)) } } // Foot @@ -714,8 +714,8 @@ impl HumArmorFootSpec { body: &Body, loadout: &Loadout, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Foot(foot), .. @@ -748,15 +748,15 @@ impl HumArmorFootSpec { foot_segment = foot_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(foot_color))); } - generate_mesh(&foot_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(foot_segment, Vec3::from(spec.vox_spec.1)) } pub fn mesh_left_foot( &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { self.mesh_foot(body, loadout, true, generate_mesh) } @@ -764,8 +764,8 @@ impl HumArmorFootSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { self.mesh_foot(body, loadout, false, generate_mesh) } } @@ -779,12 +779,12 @@ impl HumMainWeaponSpec { pub fn mesh_main_weapon( &self, item_kind: Option<&ItemKind>, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let tool_kind = if let Some(ItemKind::Tool(Tool { kind, .. })) = item_kind { kind } else { - return Mesh::new(); + return (Mesh::new() /* , Mesh::new() */, Aabb::default()); }; let spec = match self.0.get(tool_kind) { @@ -796,7 +796,7 @@ impl HumMainWeaponSpec { }; let tool_kind_segment = graceful_load_segment(&spec.vox_spec.0); - generate_mesh(&tool_kind_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(tool_kind_segment, Vec3::from(spec.vox_spec.1)) } } // Lantern @@ -809,8 +809,8 @@ impl HumArmorLanternSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Lantern(Lantern { kind, .. })) = loadout.lantern.as_ref().map(|i| &i.kind) { @@ -837,7 +837,7 @@ impl HumArmorLanternSpec { lantern_segment.map_rgb(|rgb| recolor_grey(rgb, Rgb::from(lantern_color))); } - generate_mesh(&lantern_segment, Vec3::from(spec.vox_spec.1)) + generate_mesh(lantern_segment, Vec3::from(spec.vox_spec.1)) } } impl HumArmorHeadSpec { @@ -850,8 +850,8 @@ impl HumArmorHeadSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Head(head), .. @@ -892,7 +892,7 @@ impl HumArmorHeadSpec { .unify() .0; - generate_mesh(&head, Vec3::from(spec.vox_spec.1)) + generate_mesh(head, Vec3::from(spec.vox_spec.1)) } } impl HumArmorTabardSpec { @@ -905,8 +905,8 @@ impl HumArmorTabardSpec { &self, body: &Body, loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = if let Some(ItemKind::Armor { kind: Armor::Tabard(tabard), .. @@ -947,13 +947,11 @@ impl HumArmorTabardSpec { .unify() .0; - generate_mesh(&tabard, Vec3::from(spec.vox_spec.1)) + generate_mesh(tabard, Vec3::from(spec.vox_spec.1)) } } // TODO: Inventory -pub fn mesh_glider( - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { +pub fn mesh_glider(generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes) -> BoneMeshes { load_mesh( "object.glider", Vec3::new(-26.0, -26.0, -5.0), @@ -1018,8 +1016,8 @@ impl QuadrupedSmallCentralSpec { &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1032,15 +1030,15 @@ impl QuadrupedSmallCentralSpec { }; let central = graceful_load_segment(&spec.head.central.0); - generate_mesh(¢ral, Vec3::from(spec.head.offset)) + generate_mesh(central, Vec3::from(spec.head.offset)) } pub fn mesh_chest( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1053,7 +1051,7 @@ impl QuadrupedSmallCentralSpec { }; let central = graceful_load_segment(&spec.chest.central.0); - generate_mesh(¢ral, Vec3::from(spec.chest.offset)) + generate_mesh(central, Vec3::from(spec.chest.offset)) } } @@ -1067,8 +1065,8 @@ impl QuadrupedSmallLateralSpec { &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1081,15 +1079,15 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.left_front.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.left_front.offset)) + generate_mesh(lateral, Vec3::from(spec.left_front.offset)) } pub fn mesh_foot_rf( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1102,15 +1100,15 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.right_front.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.right_front.offset)) + generate_mesh(lateral, Vec3::from(spec.right_front.offset)) } pub fn mesh_foot_lb( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1123,15 +1121,15 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.left_back.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.left_back.offset)) + generate_mesh(lateral, Vec3::from(spec.left_back.offset)) } pub fn mesh_foot_rb( &self, species: QSSpecies, body_type: QSBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1144,7 +1142,7 @@ impl QuadrupedSmallLateralSpec { }; let lateral = graceful_load_segment(&spec.right_back.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.right_back.offset)) + generate_mesh(lateral, Vec3::from(spec.right_back.offset)) } } @@ -1209,8 +1207,8 @@ impl QuadrupedMediumCentralSpec { &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1223,15 +1221,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.upper.central.0); - generate_mesh(¢ral, Vec3::from(spec.upper.offset)) + generate_mesh(central, Vec3::from(spec.upper.offset)) } pub fn mesh_head_lower( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1244,15 +1242,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.lower.central.0); - generate_mesh(¢ral, Vec3::from(spec.lower.offset)) + generate_mesh(central, Vec3::from(spec.lower.offset)) } pub fn mesh_jaw( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1265,15 +1263,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.jaw.central.0); - generate_mesh(¢ral, Vec3::from(spec.jaw.offset)) + generate_mesh(central, Vec3::from(spec.jaw.offset)) } pub fn mesh_ears( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1286,15 +1284,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.ears.central.0); - generate_mesh(¢ral, Vec3::from(spec.ears.offset)) + generate_mesh(central, Vec3::from(spec.ears.offset)) } pub fn mesh_torso_f( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1307,15 +1305,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.torso_f.central.0); - generate_mesh(¢ral, Vec3::from(spec.torso_f.offset)) + generate_mesh(central, Vec3::from(spec.torso_f.offset)) } pub fn mesh_torso_b( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1328,15 +1326,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.torso_b.central.0); - generate_mesh(¢ral, Vec3::from(spec.torso_b.offset)) + generate_mesh(central, Vec3::from(spec.torso_b.offset)) } pub fn mesh_tail( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1349,7 +1347,7 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.tail.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail.offset)) + generate_mesh(central, Vec3::from(spec.tail.offset)) } } @@ -1363,8 +1361,8 @@ impl QuadrupedMediumLateralSpec { &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1377,15 +1375,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.left_front.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.left_front.offset)) + generate_mesh(lateral, Vec3::from(spec.left_front.offset)) } pub fn mesh_foot_rf( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1398,15 +1396,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.right_front.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.right_front.offset)) + generate_mesh(lateral, Vec3::from(spec.right_front.offset)) } pub fn mesh_foot_lb( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1419,15 +1417,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.left_back.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.left_back.offset)) + generate_mesh(lateral, Vec3::from(spec.left_back.offset)) } pub fn mesh_foot_rb( &self, species: QMSpecies, body_type: QMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1440,7 +1438,7 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.right_back.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.right_back.offset)) + generate_mesh(lateral, Vec3::from(spec.right_back.offset)) } } @@ -1502,8 +1500,8 @@ impl BirdMediumCenterSpec { &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1516,15 +1514,15 @@ impl BirdMediumCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_torso( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1537,15 +1535,15 @@ impl BirdMediumCenterSpec { }; let center = graceful_load_segment(&spec.torso.center.0); - generate_mesh(¢er, Vec3::from(spec.torso.offset)) + generate_mesh(center, Vec3::from(spec.torso.offset)) } pub fn mesh_tail( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1558,7 +1556,7 @@ impl BirdMediumCenterSpec { }; let center = graceful_load_segment(&spec.tail.center.0); - generate_mesh(¢er, Vec3::from(spec.tail.offset)) + generate_mesh(center, Vec3::from(spec.tail.offset)) } } impl BirdMediumLateralSpec { @@ -1571,8 +1569,8 @@ impl BirdMediumLateralSpec { &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1585,15 +1583,15 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_l.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_l.offset)) } pub fn mesh_wing_r( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1606,15 +1604,15 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_r.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_r.offset)) } pub fn mesh_foot_l( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1627,15 +1625,15 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_l.offset)) } pub fn mesh_foot_r( &self, species: BMSpecies, body_type: BMBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1648,7 +1646,7 @@ impl BirdMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_r.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_r.offset)) } } //// @@ -1686,8 +1684,8 @@ impl CritterCenterSpec { &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1700,15 +1698,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_chest( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1721,15 +1719,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.chest.center.0); - generate_mesh(¢er, Vec3::from(spec.chest.offset)) + generate_mesh(center, Vec3::from(spec.chest.offset)) } pub fn mesh_feet_f( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1742,15 +1740,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.feet_f.center.0); - generate_mesh(¢er, Vec3::from(spec.feet_f.offset)) + generate_mesh(center, Vec3::from(spec.feet_f.offset)) } pub fn mesh_feet_b( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1763,15 +1761,15 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.feet_b.center.0); - generate_mesh(¢er, Vec3::from(spec.feet_b.offset)) + generate_mesh(center, Vec3::from(spec.feet_b.offset)) } pub fn mesh_tail( &self, species: CSpecies, body_type: CBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1784,14 +1782,14 @@ impl CritterCenterSpec { }; let center = graceful_load_segment(&spec.tail.center.0); - generate_mesh(¢er, Vec3::from(spec.tail.offset)) + generate_mesh(center, Vec3::from(spec.tail.offset)) } } //// pub fn mesh_fish_medium_head( head: fish_medium::Head, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match head { fish_medium::Head::Default => "npc.marlin.head", @@ -1803,8 +1801,8 @@ pub fn mesh_fish_medium_head( pub fn mesh_fish_medium_torso( torso: fish_medium::Torso, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match torso { fish_medium::Torso::Default => "npc.marlin.torso", @@ -1816,8 +1814,8 @@ pub fn mesh_fish_medium_torso( pub fn mesh_fish_medium_rear( rear: fish_medium::Rear, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match rear { fish_medium::Rear::Default => "npc.marlin.rear", @@ -1829,8 +1827,8 @@ pub fn mesh_fish_medium_rear( pub fn mesh_fish_medium_tail( tail: fish_medium::Tail, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match tail { fish_medium::Tail::Default => "npc.marlin.tail", @@ -1842,8 +1840,8 @@ pub fn mesh_fish_medium_tail( pub fn mesh_fish_medium_fin_l( fin_l: fish_medium::FinL, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match fin_l { fish_medium::FinL::Default => "npc.marlin.fin_l", @@ -1855,8 +1853,8 @@ pub fn mesh_fish_medium_fin_l( pub fn mesh_fish_medium_fin_r( fin_r: fish_medium::FinR, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match fin_r { fish_medium::FinR::Default => "npc.marlin.fin_r", @@ -1932,8 +1930,8 @@ impl DragonCenterSpec { &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1946,15 +1944,15 @@ impl DragonCenterSpec { }; let central = graceful_load_segment(&spec.upper.center.0); - generate_mesh(¢ral, Vec3::from(spec.upper.offset)) + generate_mesh(central, Vec3::from(spec.upper.offset)) } pub fn mesh_head_lower( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1967,15 +1965,15 @@ impl DragonCenterSpec { }; let central = graceful_load_segment(&spec.lower.center.0); - generate_mesh(¢ral, Vec3::from(spec.lower.offset)) + generate_mesh(central, Vec3::from(spec.lower.offset)) } pub fn mesh_jaw( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1988,15 +1986,15 @@ impl DragonCenterSpec { }; let central = graceful_load_segment(&spec.jaw.center.0); - generate_mesh(¢ral, Vec3::from(spec.jaw.offset)) + generate_mesh(central, Vec3::from(spec.jaw.offset)) } pub fn mesh_chest_front( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2009,15 +2007,15 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.chest_front.center.0); - generate_mesh(¢er, Vec3::from(spec.chest_front.offset)) + generate_mesh(center, Vec3::from(spec.chest_front.offset)) } pub fn mesh_chest_rear( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2030,15 +2028,15 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.chest_rear.center.0); - generate_mesh(¢er, Vec3::from(spec.chest_rear.offset)) + generate_mesh(center, Vec3::from(spec.chest_rear.offset)) } pub fn mesh_tail_front( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2051,15 +2049,15 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.tail_front.center.0); - generate_mesh(¢er, Vec3::from(spec.tail_front.offset)) + generate_mesh(center, Vec3::from(spec.tail_front.offset)) } pub fn mesh_tail_rear( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2072,7 +2070,7 @@ impl DragonCenterSpec { }; let center = graceful_load_segment(&spec.tail_rear.center.0); - generate_mesh(¢er, Vec3::from(spec.tail_rear.offset)) + generate_mesh(center, Vec3::from(spec.tail_rear.offset)) } } impl DragonLateralSpec { @@ -2084,8 +2082,8 @@ impl DragonLateralSpec { &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2098,15 +2096,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_in_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_in_l.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_in_l.offset)) } pub fn mesh_wing_in_r( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2119,15 +2117,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_in_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_in_r.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_in_r.offset)) } pub fn mesh_wing_out_l( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2140,15 +2138,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_out_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_out_l.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_out_l.offset)) } pub fn mesh_wing_out_r( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2161,15 +2159,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.wing_out_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.wing_out_r.offset)) + generate_mesh(lateral, Vec3::from(spec.wing_out_r.offset)) } pub fn mesh_foot_fl( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2182,15 +2180,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_fl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_fl.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_fl.offset)) } pub fn mesh_foot_fr( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2203,15 +2201,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_fr.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_fr.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_fr.offset)) } pub fn mesh_foot_bl( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2224,15 +2222,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_bl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_bl.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_bl.offset)) } pub fn mesh_foot_br( &self, species: DSpecies, body_type: DBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2245,15 +2243,15 @@ impl DragonLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_br.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_br.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_br.offset)) } } //// pub fn mesh_bird_small_head( head: bird_small::Head, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match head { bird_small::Head::Default => "npc.crow.head", @@ -2265,8 +2263,8 @@ pub fn mesh_bird_small_head( pub fn mesh_bird_small_torso( torso: bird_small::Torso, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match torso { bird_small::Torso::Default => "npc.crow.torso", @@ -2278,8 +2276,8 @@ pub fn mesh_bird_small_torso( pub fn mesh_bird_small_wing_l( wing_l: bird_small::WingL, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match wing_l { bird_small::WingL::Default => "npc.crow.wing_l", @@ -2291,8 +2289,8 @@ pub fn mesh_bird_small_wing_l( pub fn mesh_bird_small_wing_r( wing_r: bird_small::WingR, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match wing_r { bird_small::WingR::Default => "npc.crow.wing_r", @@ -2304,8 +2302,8 @@ pub fn mesh_bird_small_wing_r( //// pub fn mesh_fish_small_torso( torso: fish_small::Torso, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match torso { fish_small::Torso::Default => "npc.cardinalfish.torso", @@ -2317,8 +2315,8 @@ pub fn mesh_fish_small_torso( pub fn mesh_fish_small_tail( tail: fish_small::Tail, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { load_mesh( match tail { fish_small::Tail::Default => "npc.cardinalfish.tail", @@ -2389,8 +2387,8 @@ impl BipedLargeCenterSpec { &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2403,15 +2401,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_torso_upper( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2424,15 +2422,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.torso_upper.center.0); - generate_mesh(¢er, Vec3::from(spec.torso_upper.offset)) + generate_mesh(center, Vec3::from(spec.torso_upper.offset)) } pub fn mesh_torso_lower( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2445,7 +2443,7 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.torso_lower.center.0); - generate_mesh(¢er, Vec3::from(spec.torso_lower.offset)) + generate_mesh(center, Vec3::from(spec.torso_lower.offset)) } } impl BipedLargeLateralSpec { @@ -2458,8 +2456,8 @@ impl BipedLargeLateralSpec { &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2472,15 +2470,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_l.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_l.offset)) } pub fn mesh_shoulder_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2493,15 +2491,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_r.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_r.offset)) } pub fn mesh_hand_l( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2514,15 +2512,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_l.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_l.offset)) } pub fn mesh_hand_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2535,15 +2533,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_r.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_r.offset)) } pub fn mesh_leg_l( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2556,15 +2554,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_l.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_l.offset)) } pub fn mesh_leg_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2577,15 +2575,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_r.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_r.offset)) } pub fn mesh_foot_l( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2598,15 +2596,15 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_l.offset)) } pub fn mesh_foot_r( &self, species: BLSpecies, body_type: BLBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2619,7 +2617,7 @@ impl BipedLargeLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_r.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_r.offset)) } } //// @@ -2682,8 +2680,8 @@ impl GolemCenterSpec { &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2696,15 +2694,15 @@ impl GolemCenterSpec { }; let center = graceful_load_segment(&spec.head.center.0); - generate_mesh(¢er, Vec3::from(spec.head.offset)) + generate_mesh(center, Vec3::from(spec.head.offset)) } pub fn mesh_torso_upper( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2717,7 +2715,7 @@ impl GolemCenterSpec { }; let center = graceful_load_segment(&spec.torso_upper.center.0); - generate_mesh(¢er, Vec3::from(spec.torso_upper.offset)) + generate_mesh(center, Vec3::from(spec.torso_upper.offset)) } } impl GolemLateralSpec { @@ -2729,8 +2727,8 @@ impl GolemLateralSpec { &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2743,15 +2741,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_l.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_l.offset)) } pub fn mesh_shoulder_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2764,15 +2762,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.shoulder_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.shoulder_r.offset)) + generate_mesh(lateral, Vec3::from(spec.shoulder_r.offset)) } pub fn mesh_hand_l( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2785,15 +2783,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_l.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_l.offset)) } pub fn mesh_hand_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2806,15 +2804,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.hand_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.hand_r.offset)) + generate_mesh(lateral, Vec3::from(spec.hand_r.offset)) } pub fn mesh_leg_l( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2827,15 +2825,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_l.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_l.offset)) } pub fn mesh_leg_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2848,15 +2846,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_r.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_r.offset)) } pub fn mesh_foot_l( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2869,15 +2867,15 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_l.offset)) } pub fn mesh_foot_r( &self, species: GSpecies, body_type: GBodyType, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -2890,13 +2888,13 @@ impl GolemLateralSpec { }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.foot_r.offset)) + generate_mesh(lateral, Vec3::from(spec.foot_r.offset)) } } pub fn mesh_object( obj: object::Body, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { use object::Body; let (name, offset) = match obj { diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 13ada9d1da..a622889f7a 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -13,7 +13,11 @@ use crate::{ quadruped_small::QuadrupedSmallSkeleton, Animation, Skeleton, }, ecs::comp::Interpolated, - render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow}, + mesh::greedy::GreedyMesh, + render::{ + self, BoneMeshes, ColLightFmt, Consts, FigureBoneData, FigureLocals, FigureModel, Globals, + Light, RenderError, Renderer, Shadow, ShadowLocals, ShadowPipeline, Texture, + }, scene::{ camera::{Camera, CameraMode}, LodData, SceneData, @@ -29,8 +33,14 @@ use common::{ terrain::TerrainChunk, vol::RectRasterableVol, }; +use core::{ + borrow::Borrow, + hash::Hash, + ops::{Deref, DerefMut}, +}; +use guillotiere::AtlasAllocator; use hashbrown::HashMap; -use log::trace; + use specs::{Entity as EcsEntity, Join, WorldExt}; use treeculler::{BVol, BoundingSphere}; use vek::*; @@ -39,18 +49,7 @@ const DAMAGE_FADE_COEFFICIENT: f64 = 5.0; const MOVING_THRESHOLD: f32 = 0.7; const MOVING_THRESHOLD_SQR: f32 = MOVING_THRESHOLD * MOVING_THRESHOLD; -pub struct FigureMgr { - model_cache: FigureModelCache, - critter_model_cache: FigureModelCache, - quadruped_small_model_cache: FigureModelCache, - quadruped_medium_model_cache: FigureModelCache, - bird_medium_model_cache: FigureModelCache, - bird_small_model_cache: FigureModelCache, - dragon_model_cache: FigureModelCache, - fish_medium_model_cache: FigureModelCache, - fish_small_model_cache: FigureModelCache, - biped_large_model_cache: FigureModelCache, - golem_model_cache: FigureModelCache, +struct FigureMgrStates { character_states: HashMap>, quadruped_small_states: HashMap>, quadruped_medium_states: HashMap>, @@ -65,20 +64,9 @@ pub struct FigureMgr { object_states: HashMap>, } -impl FigureMgr { - pub fn new() -> Self { +impl FigureMgrStates { + fn new() -> Self { Self { - model_cache: FigureModelCache::new(), - critter_model_cache: FigureModelCache::new(), - quadruped_small_model_cache: FigureModelCache::new(), - quadruped_medium_model_cache: FigureModelCache::new(), - bird_medium_model_cache: FigureModelCache::new(), - bird_small_model_cache: FigureModelCache::new(), - dragon_model_cache: FigureModelCache::new(), - fish_medium_model_cache: FigureModelCache::new(), - fish_small_model_cache: FigureModelCache::new(), - biped_large_model_cache: FigureModelCache::new(), - golem_model_cache: FigureModelCache::new(), character_states: HashMap::new(), quadruped_small_states: HashMap::new(), quadruped_medium_states: HashMap::new(), @@ -94,18 +82,240 @@ impl FigureMgr { } } + /* fn get<'a, Q: ?Sized>(&'a self, body: &Body, entity: &Q) -> Option<&'a FigureStateMeta> + where + EcsEntity: Borrow, + Q: Hash + Eq, + { + match body { + Body::Humanoid(_) => self.character_states.get(&entity).map(Deref::deref), + Body::QuadrupedSmall(_) => self.quadruped_small_states.get(&entity).map(Deref::deref), + Body::QuadrupedMedium(_) => self.quadruped_medium_states.get(&entity).map(Deref::deref), + Body::BirdMedium(_) => self.bird_medium_states.get(&entity).map(Deref::deref), + Body::FishMedium(_) => self.fish_medium_states.get(&entity).map(Deref::deref), + Body::Critter(_) => self.critter_states.get(&entity).map(Deref::deref), + Body::Dragon(_) => self.dragon_states.get(&entity).map(Deref::deref), + Body::BirdSmall(_) => self.bird_small_states.get(&entity).map(Deref::deref), + Body::FishSmall(_) => self.fish_small_states.get(&entity).map(Deref::deref), + Body::BipedLarge(_) => self.biped_large_states.get(&entity).map(Deref::deref), + Body::Golem(_) => self.golem_states.get(&entity).map(Deref::deref), + Body::Object(_) => self.object_states.get(&entity).map(Deref::deref), + } + } */ + + fn get_mut<'a, Q: ?Sized>( + &'a mut self, + body: &Body, + entity: &Q, + ) -> Option<&'a mut FigureStateMeta> + where + EcsEntity: Borrow, + Q: Hash + Eq, + { + match body { + Body::Humanoid(_) => self + .character_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::QuadrupedSmall(_) => self + .quadruped_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::QuadrupedMedium(_) => self + .quadruped_medium_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::BirdMedium(_) => self + .bird_medium_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::FishMedium(_) => self + .fish_medium_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::Critter(_) => self + .critter_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::Dragon(_) => self.dragon_states.get_mut(&entity).map(DerefMut::deref_mut), + Body::BirdSmall(_) => self + .bird_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::FishSmall(_) => self + .fish_small_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::BipedLarge(_) => self + .biped_large_states + .get_mut(&entity) + .map(DerefMut::deref_mut), + Body::Golem(_) => self.golem_states.get_mut(&entity).map(DerefMut::deref_mut), + Body::Object(_) => self.object_states.get_mut(&entity).map(DerefMut::deref_mut), + } + } + + fn remove<'a, Q: ?Sized>(&'a mut self, body: &Body, entity: &Q) -> Option + where + EcsEntity: Borrow, + Q: Hash + Eq, + { + match body { + Body::Humanoid(_) => self.character_states.remove(&entity).map(|e| e.meta), + Body::QuadrupedSmall(_) => self.quadruped_small_states.remove(&entity).map(|e| e.meta), + Body::QuadrupedMedium(_) => { + self.quadruped_medium_states.remove(&entity).map(|e| e.meta) + }, + Body::BirdMedium(_) => self.bird_medium_states.remove(&entity).map(|e| e.meta), + Body::FishMedium(_) => self.fish_medium_states.remove(&entity).map(|e| e.meta), + Body::Critter(_) => self.critter_states.remove(&entity).map(|e| e.meta), + Body::Dragon(_) => self.dragon_states.remove(&entity).map(|e| e.meta), + Body::BirdSmall(_) => self.bird_small_states.remove(&entity).map(|e| e.meta), + Body::FishSmall(_) => self.fish_small_states.remove(&entity).map(|e| e.meta), + Body::BipedLarge(_) => self.biped_large_states.remove(&entity).map(|e| e.meta), + Body::Golem(_) => self.golem_states.remove(&entity).map(|e| e.meta), + Body::Object(_) => self.object_states.remove(&entity).map(|e| e.meta), + } + } + + fn retain<'a>(&'a mut self, mut f: impl FnMut(&EcsEntity, &mut FigureStateMeta) -> bool) { + self.character_states.retain(|k, v| f(k, &mut *v)); + self.quadruped_small_states.retain(|k, v| f(k, &mut *v)); + self.quadruped_medium_states.retain(|k, v| f(k, &mut *v)); + self.bird_medium_states.retain(|k, v| f(k, &mut *v)); + self.fish_medium_states.retain(|k, v| f(k, &mut *v)); + self.critter_states.retain(|k, v| f(k, &mut *v)); + self.dragon_states.retain(|k, v| f(k, &mut *v)); + self.bird_small_states.retain(|k, v| f(k, &mut *v)); + self.fish_small_states.retain(|k, v| f(k, &mut *v)); + self.biped_large_states.retain(|k, v| f(k, &mut *v)); + self.golem_states.retain(|k, v| f(k, &mut *v)); + self.object_states.retain(|k, v| f(k, &mut *v)); + } + + fn count(&self) -> usize { + self.character_states.len() + + self.quadruped_small_states.len() + + self.character_states.len() + + self.quadruped_medium_states.len() + + self.bird_medium_states.len() + + self.fish_medium_states.len() + + self.critter_states.len() + + self.dragon_states.len() + + self.bird_small_states.len() + + self.fish_small_states.len() + + self.biped_large_states.len() + + self.golem_states.len() + + self.object_states.len() + } + + fn count_visible(&self) -> usize { + self.character_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .quadruped_small_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .quadruped_medium_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .bird_medium_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .critter_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self.dragon_states.iter().filter(|(_, c)| c.visible).count() + + self + .fish_medium_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .bird_small_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .fish_small_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self + .biped_large_states + .iter() + .filter(|(_, c)| c.visible) + .count() + + self.golem_states.iter().filter(|(_, c)| c.visible).count() + + self.object_states.iter().filter(|(_, c)| c.visible).count() + } +} + +pub struct FigureMgr { + col_lights: FigureColLights, + model_cache: FigureModelCache, + critter_model_cache: FigureModelCache, + quadruped_small_model_cache: FigureModelCache, + quadruped_medium_model_cache: FigureModelCache, + bird_medium_model_cache: FigureModelCache, + bird_small_model_cache: FigureModelCache, + dragon_model_cache: FigureModelCache, + fish_medium_model_cache: FigureModelCache, + fish_small_model_cache: FigureModelCache, + biped_large_model_cache: FigureModelCache, + golem_model_cache: FigureModelCache, + states: FigureMgrStates, +} + +impl FigureMgr { + pub fn new(renderer: &mut Renderer) -> Self { + Self { + col_lights: FigureColLights::new(renderer), + model_cache: FigureModelCache::new(), + critter_model_cache: FigureModelCache::new(), + quadruped_small_model_cache: FigureModelCache::new(), + quadruped_medium_model_cache: FigureModelCache::new(), + bird_medium_model_cache: FigureModelCache::new(), + bird_small_model_cache: FigureModelCache::new(), + dragon_model_cache: FigureModelCache::new(), + fish_medium_model_cache: FigureModelCache::new(), + fish_small_model_cache: FigureModelCache::new(), + biped_large_model_cache: FigureModelCache::new(), + golem_model_cache: FigureModelCache::new(), + states: FigureMgrStates::new(), + } + } + + pub fn col_lights(&self) -> &FigureColLights { &self.col_lights } + pub fn clean(&mut self, tick: u64) { - self.model_cache.clean(tick); - self.critter_model_cache.clean(tick); - self.quadruped_small_model_cache.clean(tick); - self.quadruped_medium_model_cache.clean(tick); - self.bird_medium_model_cache.clean(tick); - self.bird_small_model_cache.clean(tick); - self.dragon_model_cache.clean(tick); - self.fish_medium_model_cache.clean(tick); - self.fish_small_model_cache.clean(tick); - self.biped_large_model_cache.clean(tick); - self.golem_model_cache.clean(tick); + self.model_cache.clean(&mut self.col_lights, tick); + self.critter_model_cache.clean(&mut self.col_lights, tick); + self.quadruped_small_model_cache + .clean(&mut self.col_lights, tick); + self.quadruped_medium_model_cache + .clean(&mut self.col_lights, tick); + self.bird_medium_model_cache + .clean(&mut self.col_lights, tick); + self.bird_small_model_cache + .clean(&mut self.col_lights, tick); + self.dragon_model_cache.clean(&mut self.col_lights, tick); + self.fish_medium_model_cache + .clean(&mut self.col_lights, tick); + self.fish_small_model_cache + .clean(&mut self.col_lights, tick); + self.biped_large_model_cache + .clean(&mut self.col_lights, tick); + self.golem_model_cache.clean(&mut self.col_lights, tick); } pub fn update_lighting(&mut self, scene_data: &SceneData) { @@ -116,7 +326,7 @@ impl FigureMgr { let mut anim_storage = ecs.write_storage::(); if let None = anim_storage.get_mut(entity) { let anim = LightAnimation { - offset: Vec3::zero(), + offset: Vec3::zero(), //Vec3::new(0.0, 0.0, 2.0), col: light_emitter.col, strength: 0.0, }; @@ -150,7 +360,7 @@ impl FigureMgr { if let Some(_) = waypoint { light_anim.offset = Vec3::unit_z() * 0.5; } - if let Some(state) = self.character_states.get(&entity) { + if let Some(state) = self.states.character_states.get(&entity) { light_anim.offset = state.lantern_offset; } if !light_anim.strength.is_finite() { @@ -170,7 +380,12 @@ impl FigureMgr { } } - pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData, camera: &Camera) { + pub fn maintain( + &mut self, + renderer: &mut Renderer, + scene_data: &SceneData, + camera: &Camera, + ) -> Aabb { let state = scene_data.state; let time = state.get_time(); let tick = scene_data.tick; @@ -183,6 +398,10 @@ impl FigureMgr { .read_storage::() .get(scene_data.player_entity) .map_or(Vec3::zero(), |pos| pos.0); + let mut visible_aabb = Aabb { + min: player_pos - 2.0, + max: player_pos + 2.0, + }; for ( i, @@ -215,6 +434,37 @@ impl FigureMgr { .join() .enumerate() { + let is_player = scene_data.player_entity == entity; + let (pos, ori) = interpolated + .map(|i| (Pos(i.pos), *i.ori)) + .unwrap_or((*pos, Vec3::unit_y())); + + // Don't display figures outside the frustum spectrum (this is important to do + // for any figure that potentially casts a shadow, since we use this + // to estimate bounds for shadow maps). The reason we do this + // before the udpate cull is to make sure infrequently updated + // figures are not clipped when they're visible; the vd_frac part is + // delayed until after that test to limit program pauses from too many + // figures being removed at once (rather than their being removed based on their + // update rates). + let radius = scale.unwrap_or(&Scale(1.0)).0 * 2.0; + let (in_frustum, lpindex) = if let Some(mut meta) = self.states.get_mut(body, &entity) { + let (in_frustum, lpindex) = BoundingSphere::new(pos.0.into_array(), radius) + .coherent_test_against_frustum(frustum, meta.lpindex); + meta.visible = in_frustum; + meta.lpindex = lpindex; + (in_frustum, lpindex) + } else { + (true, 0) + }; + if in_frustum { + // Update visible bounds. + visible_aabb.expand_to_contain(Aabb { + min: pos.0 - radius, + max: pos.0 + radius, + }); + } + // Maintaining figure data and sending new figure data to the GPU turns out to // be a very expensive operation. We want to avoid doing it as much // as possible, so we make the assumption that players don't care so @@ -233,12 +483,6 @@ impl FigureMgr { continue; } - let is_player = scene_data.player_entity == entity; - - let (pos, ori) = interpolated - .map(|i| (Pos(i.pos), *i.ori)) - .unwrap_or((*pos, Vec3::unit_y())); - // Don't process figures outside the vd let vd_frac = Vec2::from(pos.0 - player_pos) .map2(TerrainChunk::RECT_SIZE, |d: f32, sz| { @@ -248,242 +492,15 @@ impl FigureMgr { / view_distance as f32; // Keep from re-adding/removing entities on the border of the vd if vd_frac > 1.2 { - match body { - Body::Humanoid(_) => { - self.character_states.remove(&entity); - }, - Body::QuadrupedSmall(_) => { - self.quadruped_small_states.remove(&entity); - }, - Body::QuadrupedMedium(_) => { - self.quadruped_medium_states.remove(&entity); - }, - Body::BirdMedium(_) => { - self.bird_medium_states.remove(&entity); - }, - Body::FishMedium(_) => { - self.fish_medium_states.remove(&entity); - }, - Body::Critter(_) => { - self.critter_states.remove(&entity); - }, - Body::Dragon(_) => { - self.dragon_states.remove(&entity); - }, - Body::BirdSmall(_) => { - self.bird_small_states.remove(&entity); - }, - Body::FishSmall(_) => { - self.fish_small_states.remove(&entity); - }, - Body::BipedLarge(_) => { - self.biped_large_states.remove(&entity); - }, - Body::Golem(_) => { - self.biped_large_states.remove(&entity); - }, - Body::Object(_) => { - self.object_states.remove(&entity); - }, - } + self.states.remove(body, &entity); continue; } else if vd_frac > 1.0 { - match body { - Body::Humanoid(_) => { - self.character_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::QuadrupedSmall(_) => { - self.quadruped_small_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::QuadrupedMedium(_) => { - self.quadruped_medium_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::BirdMedium(_) => { - self.bird_medium_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::FishMedium(_) => { - self.fish_medium_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Critter(_) => { - self.critter_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Dragon(_) => { - self.dragon_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::BirdSmall(_) => { - self.bird_small_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::FishSmall(_) => { - self.fish_small_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::BipedLarge(_) => { - self.biped_large_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Golem(_) => { - self.golem_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - Body::Object(_) => { - self.object_states - .get_mut(&entity) - .map(|state| state.visible = false); - }, - } + self.states + .get_mut(body, &entity) + .map(|state| state.visible = false); continue; } - // Don't process figures outside the frustum spectrum - let (in_frustum, lpindex) = - BoundingSphere::new(pos.0.into_array(), scale.unwrap_or(&Scale(1.0)).0 * 2.0) - .coherent_test_against_frustum( - &frustum, - match body { - Body::Humanoid(_) => self - .character_states - .get(&entity) - .map(|state| state.lpindex), - Body::QuadrupedSmall(_) => self - .quadruped_small_states - .get(&entity) - .map(|state| state.lpindex), - Body::QuadrupedMedium(_) => self - .quadruped_medium_states - .get(&entity) - .map(|state| state.lpindex), - Body::BirdMedium(_) => self - .bird_medium_states - .get(&entity) - .map(|state| state.lpindex), - Body::FishMedium(_) => self - .fish_medium_states - .get(&entity) - .map(|state| state.lpindex), - Body::Critter(_) => { - self.critter_states.get(&entity).map(|state| state.lpindex) - }, - Body::Dragon(_) => { - self.dragon_states.get(&entity).map(|state| state.lpindex) - }, - Body::BirdSmall(_) => self - .bird_small_states - .get(&entity) - .map(|state| state.lpindex), - Body::FishSmall(_) => self - .fish_small_states - .get(&entity) - .map(|state| state.lpindex), - Body::BipedLarge(_) => self - .biped_large_states - .get(&entity) - .map(|state| state.lpindex), - Body::Golem(_) => { - self.golem_states.get(&entity).map(|state| state.lpindex) - }, - Body::Object(_) => { - self.object_states.get(&entity).map(|state| state.lpindex) - }, - } - .unwrap_or(0), - ); - - if !in_frustum { - match body { - Body::Humanoid(_) => { - self.character_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::QuadrupedSmall(_) => { - self.quadruped_small_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::QuadrupedMedium(_) => { - self.quadruped_medium_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::BirdMedium(_) => { - self.bird_medium_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::FishMedium(_) => { - self.fish_medium_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Critter(_) => { - self.critter_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Dragon(_) => { - self.dragon_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::BirdSmall(_) => { - self.bird_small_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::FishSmall(_) => { - self.fish_small_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::BipedLarge(_) => { - self.biped_large_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Golem(_) => { - self.golem_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - Body::Object(_) => { - self.object_states.get_mut(&entity).map(|state| { - state.lpindex = lpindex; - state.visible = false - }); - }, - } - } - // Change in health as color! let col = stats .map(|s| { @@ -509,19 +526,18 @@ impl FigureMgr { match body { Body::Humanoid(_) => { - let skeleton_attr = &self - .model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .character_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); @@ -733,25 +749,27 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::QuadrupedSmall(_) => { - let skeleton_attr = &self - .quadruped_small_model_cache - .get_or_create_model( + let (model, skeleton_attr) = + self.quadruped_small_model_cache.get_or_create_model( renderer, + &mut self.col_lights, *body, loadout, tick, CameraMode::default(), None, - ) - .1; + ); let state = self + .states .quadruped_small_states .entry(entity) .or_insert_with(|| { @@ -814,25 +832,27 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::QuadrupedMedium(_) => { - let skeleton_attr = &self - .quadruped_medium_model_cache - .get_or_create_model( + let (model, skeleton_attr) = + self.quadruped_medium_model_cache.get_or_create_model( renderer, + &mut self.col_lights, *body, loadout, tick, CameraMode::default(), None, - ) - .1; + ); let state = self + .states .quadruped_medium_states .entry(entity) .or_insert_with(|| { @@ -897,25 +917,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::BirdMedium(_) => { - let skeleton_attr = &self - .bird_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.bird_medium_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .bird_medium_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, BirdMediumSkeleton::new())); @@ -972,25 +993,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::FishMedium(_) => { - let skeleton_attr = &self - .fish_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.fish_medium_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .fish_medium_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, FishMediumSkeleton::new())); @@ -1047,25 +1069,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::Dragon(_) => { - let skeleton_attr = &self - .dragon_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.dragon_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .dragon_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, DragonSkeleton::new())); @@ -1122,25 +1145,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::Critter(_) => { - let skeleton_attr = &self - .critter_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.critter_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .critter_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, CritterSkeleton::new())); @@ -1197,25 +1221,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::BirdSmall(_) => { - let skeleton_attr = &self - .bird_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.bird_small_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .bird_small_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, BirdSmallSkeleton::new())); @@ -1272,25 +1297,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::FishSmall(_) => { - let skeleton_attr = &self - .fish_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.fish_small_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .fish_small_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, FishSmallSkeleton::new())); @@ -1347,25 +1373,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::BipedLarge(_) => { - let skeleton_attr = &self - .biped_large_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.biped_large_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .biped_large_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, BipedLargeSkeleton::new())); @@ -1422,25 +1449,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::Golem(_) => { - let skeleton_attr = &self - .golem_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - CameraMode::default(), - None, - ) - .1; + let (model, skeleton_attr) = self.golem_model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); let state = self + .states .golem_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, GolemSkeleton::new())); @@ -1497,13 +1525,26 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, - true, + in_frustum, is_player, + camera, ); }, Body::Object(_) => { + let (model, _) = &self.model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + CameraMode::default(), + None, + ); + let state = self + .states .object_states .entry(entity) .or_insert_with(|| FigureState::new(renderer, ObjectSkeleton::new())); @@ -1517,9 +1558,11 @@ impl FigureMgr { col, dt, state_animation_rate, + &model[0], lpindex, true, is_player, + camera, ); }, } @@ -1529,30 +1572,63 @@ impl FigureMgr { self.update_lighting(scene_data); // Clear states that have deleted entities. - self.character_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.quadruped_small_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.quadruped_medium_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.bird_medium_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.fish_medium_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.critter_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.dragon_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.bird_small_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.fish_small_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.biped_large_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.golem_states - .retain(|entity, _| ecs.entities().is_alive(*entity)); - self.object_states + self.states .retain(|entity, _| ecs.entities().is_alive(*entity)); + + visible_aabb + } + + pub fn render_shadows( + &mut self, + renderer: &mut Renderer, + state: &State, + tick: u64, + globals: &Consts, + shadow_mats: &Consts, + is_daylight: bool, + _light_data: &[Light], + camera: &Camera, + figure_lod_render_distance: f32, + ) { + let ecs = state.ecs(); + + if is_daylight && renderer.render_mode().shadow == render::ShadowMode::Map { + ( + &ecs.entities(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ) + .join() + // Don't render dead entities + .filter(|(_, _, _, _, stats, _, _)| stats.map_or(true, |s| !s.is_dead)) + .for_each(|(entity, pos, _, body, _, loadout, _)| { + if let Some((locals, bone_consts, model, _)) = self.get_model_for_render( + renderer, + tick, + camera, + None, + entity, + body, + loadout, + false, + pos.0, + figure_lod_render_distance, + |state| state.visible, + ) { + renderer.render_figure_shadow_directed( + model, + globals, + locals, + bone_consts, + shadow_mats, + ); + } + }); + } } pub fn render( @@ -1564,6 +1640,7 @@ impl FigureMgr { globals: &Consts, lights: &Consts, shadows: &Consts, + shadow_mats: &Consts, lod: &LodData, camera: &Camera, figure_lod_render_distance: f32, @@ -1589,13 +1666,9 @@ impl FigureMgr { let is_player = entity == player_entity; if !is_player { - self.render_figure( + if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( renderer, tick, - globals, - lights, - shadows, - lod, camera, character_state, entity, @@ -1604,7 +1677,21 @@ impl FigureMgr { false, pos.0, figure_lod_render_distance, - ); + |state| state.visible, + ) { + renderer.render_figure( + model, + &col_lights.col_lights, + globals, + locals, + bone_consts, + lights, + shadows, + shadow_mats, + &lod.map, + &lod.horizon, + ); + } } } } @@ -1618,6 +1705,7 @@ impl FigureMgr { globals: &Consts, lights: &Consts, shadows: &Consts, + shadow_mats: &Consts, lod: &LodData, camera: &Camera, figure_lod_render_distance: f32, @@ -1641,13 +1729,9 @@ impl FigureMgr { let loadout_storage = ecs.read_storage::(); let loadout = loadout_storage.get(player_entity); - self.render_figure( + if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( renderer, tick, - globals, - lights, - shadows, - lod, camera, character_state, player_entity, @@ -1656,35 +1740,90 @@ impl FigureMgr { true, pos.0, figure_lod_render_distance, - ); + |state| state.visible, + ) { + renderer.render_player( + model, + &col_lights.col_lights, + globals, + locals, + bone_consts, + lights, + shadows, + shadow_mats, + &lod.map, + &lod.horizon, + ); + renderer.render_player_shadow( + model, + &col_lights.col_lights, + globals, + locals, + bone_consts, + lights, + shadows, + shadow_mats, + &lod.map, + &lod.horizon, + ); + } } } - fn render_figure( + /* fn do_models_for_render( + state: &State, + player_entity: EcsEntity, + ) -> impl IntoIterator<> + Clone { + let ecs = state.ecs(); + + ( + &ecs.entities(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), + ) + .join() + // Don't render dead entities + .filter(|(_, _, _, _, stats, _, _)| stats.map_or(true, |s| !s.is_dead)) + .for_each(|(entity, pos, _, body, _, loadout, _)| { + + }); + } */ + + fn get_model_for_render( &mut self, renderer: &mut Renderer, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, - lod: &LodData, camera: &Camera, character_state: Option<&CharacterState>, entity: EcsEntity, body: &Body, loadout: Option<&Loadout>, is_player: bool, + // is_shadow: bool, pos: Vec3, figure_lod_render_distance: f32, - ) { + filter_state: impl Fn(&FigureStateMeta) -> bool, + ) -> Option<( + &Consts, + &Consts, + &FigureModel, + &FigureColLights, + )> { let player_camera_mode = if is_player { camera.get_mode() } else { CameraMode::default() }; + let focus_pos = camera.get_focus_pos(); + let cam_pos = camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); let character_state = if is_player { character_state } else { None }; let FigureMgr { + col_lights: ref mut col_lights_, model_cache, critter_model_cache, quadruped_small_model_cache, @@ -1696,30 +1835,35 @@ impl FigureMgr { fish_small_model_cache, biped_large_model_cache, golem_model_cache, - character_states, - quadruped_small_states, - quadruped_medium_states, - bird_medium_states, - fish_medium_states, - critter_states, - dragon_states, - bird_small_states, - fish_small_states, - biped_large_states, - golem_states, - object_states, + states: + FigureMgrStates { + character_states, + quadruped_small_states, + quadruped_medium_states, + bird_medium_states, + fish_medium_states, + critter_states, + dragon_states, + bird_small_states, + fish_small_states, + biped_large_states, + golem_states, + object_states, + }, } = self; + let col_lights = &mut *col_lights_; if let Some((locals, bone_consts, model)) = match body { Body::Humanoid(_) => character_states .get(&entity) - .filter(|state| state.visible) - .map(|state| { + .filter(|state| filter_state(&*state)) + .map(move |state| { ( state.locals(), state.bone_consts(), &model_cache .get_or_create_model( renderer, + col_lights, *body, loadout, tick, @@ -1729,45 +1873,54 @@ impl FigureMgr { .0, ) }), - Body::QuadrupedSmall(_) => quadruped_small_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &quadruped_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::QuadrupedMedium(_) => quadruped_medium_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &quadruped_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::BirdMedium(_) => bird_medium_states.get(&entity).map(|state| { + Body::QuadrupedSmall(_) => quadruped_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &quadruped_small_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::QuadrupedMedium(_) => quadruped_medium_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &quadruped_medium_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::BirdMedium(_) => bird_medium_states.get(&entity).map(move |state| { ( state.locals(), state.bone_consts(), &bird_medium_model_cache .get_or_create_model( renderer, + col_lights, *body, loadout, tick, @@ -1777,13 +1930,14 @@ impl FigureMgr { .0, ) }), - Body::FishMedium(_) => fish_medium_states.get(&entity).map(|state| { + Body::FishMedium(_) => fish_medium_states.get(&entity).map(move |state| { ( state.locals(), state.bone_consts(), &fish_medium_model_cache .get_or_create_model( renderer, + col_lights, *body, loadout, tick, @@ -1793,262 +1947,335 @@ impl FigureMgr { .0, ) }), - Body::Critter(_) => critter_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &critter_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Dragon(_) => dragon_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &dragon_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::BirdSmall(_) => bird_small_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &bird_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::FishSmall(_) => fish_small_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &fish_small_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::BipedLarge(_) => biped_large_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &biped_large_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Golem(_) => golem_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &golem_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::Object(_) => object_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), + Body::Critter(_) => critter_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &critter_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Dragon(_) => dragon_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &dragon_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::BirdSmall(_) => bird_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &bird_small_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::FishSmall(_) => fish_small_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &fish_small_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::BipedLarge(_) => biped_large_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &biped_large_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Golem(_) => golem_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &golem_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::Object(_) => object_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), } { let figure_low_detail_distance = figure_lod_render_distance * 0.75; let figure_mid_detail_distance = figure_lod_render_distance * 0.5; - let model = if pos.distance_squared(camera.get_focus_pos()) - > figure_low_detail_distance.powf(2.0) - { + let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powf(2.0) { &model[2] - } else if pos.distance_squared(camera.get_focus_pos()) - > figure_mid_detail_distance.powf(2.0) - { + } else if pos.distance_squared(cam_pos) > figure_mid_detail_distance.powf(2.0) { &model[1] } else { &model[0] }; - if is_player { - renderer.render_player( - model, - globals, - locals, - bone_consts, - lights, - shadows, - &lod.map, - &lod.horizon, - ); - renderer.render_player_shadow( - model, - globals, - locals, - bone_consts, - lights, - shadows, - &lod.map, - &lod.horizon, - ); - } else { - renderer.render_figure( - model, - globals, - locals, - bone_consts, - lights, - shadows, - &lod.map, - &lod.horizon, - ); - } + Some((locals, bone_consts, model, &*col_lights_)) } else { - trace!("Body has no saved figure"); + // trace!("Body has no saved figure"); + None } } - pub fn figure_count(&self) -> usize { - self.character_states.len() - + self.quadruped_small_states.len() - + self.character_states.len() - + self.quadruped_medium_states.len() - + self.bird_medium_states.len() - + self.fish_medium_states.len() - + self.critter_states.len() - + self.dragon_states.len() - + self.bird_small_states.len() - + self.fish_small_states.len() - + self.biped_large_states.len() - + self.golem_states.len() - + self.object_states.len() + pub fn figure_count(&self) -> usize { self.states.count() } + + pub fn figure_count_visible(&self) -> usize { self.states.count_visible() } +} + +pub struct FigureColLights { + atlas: AtlasAllocator, + col_lights: Texture, +} + +impl FigureColLights { + pub fn new(renderer: &mut Renderer) -> Self { + let (atlas, col_lights) = + Self::make_atlas(renderer).expect("Failed to create texture atlas for figures"); + Self { atlas, col_lights } } - pub fn figure_count_visible(&self) -> usize { - self.character_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .quadruped_small_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .quadruped_medium_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .bird_medium_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .critter_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self.dragon_states.iter().filter(|(_, c)| c.visible).count() - + self - .fish_medium_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .bird_small_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .fish_small_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self - .biped_large_states - .iter() - .filter(|(_, c)| c.visible) - .count() - + self.golem_states.iter().filter(|(_, c)| c.visible).count() - + self.object_states.iter().filter(|(_, c)| c.visible).count() + pub fn texture(&self) -> &Texture { &self.col_lights } + + pub fn create_figure<'a>( + &mut self, + renderer: &mut Renderer, + greedy: GreedyMesh<'a>, + /* (opaque, shadow) */ (opaque, bounds): BoneMeshes, + ) -> Result { + // println!("Figure bounds: {:?}", bounds); + let (tex, tex_size) = greedy.finalize(); + let atlas = &mut self.atlas; + let allocation = atlas + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) + .expect("Not yet implemented: allocate new atlas on allocation faillure."); + // println!("Allocation {:?} for {:?} (original size = {:?}... ugh)", + // allocation, response.pos, tex_size); NOTE: Cast is safe since the + // origin was a u16. + let atlas_offs = Vec2::new( + allocation.rectangle.min.x as u16, + allocation.rectangle.min.y as u16, + ); + if atlas_offs == Vec2::zero() { + // println!("Model: {:?}", &response.opaque_mesh.vertices()); + // println!("Texture: {:?}", tex); + } + + /* if let Err(err) = renderer.update_texture( + &self.col_lights, + // &col_lights, + // NOTE: Cast is safe since the origin was a u16. + atlas_offs.into_array(), + tex_size.into_array(), + &tex, + ) { + panic!("Ahhh {:?}", err); + log::warn!("Failed to update texture: {:?}", err); + } */ + // FIXME: Deal with allocation failure! + /* renderer.update_texture( + &self.col_lights, + // &col_lights, + // NOTE: Cast is safe since the origin was a u16. + atlas_offs.into_array(), + tex_size.into_array(), + &tex, + )?; */ + let col_lights = ShadowPipeline::create_col_lights(renderer, (tex, tex_size))?; + + Ok(FigureModel { + bounds, + opaque: renderer.create_model(&opaque)?, + // shadow: renderer.create_model(&shadow)?, + col_lights, + allocation, + }) + } + + fn make_atlas( + renderer: &mut Renderer, + ) -> Result<(AtlasAllocator, Texture), RenderError> { + let max_texture_size = renderer.max_texture_size(); + let atlas_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + // let atlas_size = guillotiere::Size::new(1, 1); + let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { + // TODO: Verify some good empirical constants. + small_size_threshold: 32, + large_size_threshold: 256, + ..guillotiere::AllocatorOptions::default() + }); + let texture = renderer.create_texture_raw( + gfx::texture::Kind::D2( + max_texture_size, + max_texture_size, + gfx::texture::AaMode::Single, + ), + 1 as gfx::texture::Level, + // gfx::memory::Upload, + gfx::memory::Bind::SHADER_RESOURCE, /* | gfx::memory::Bind::TRANSFER_DST */ + gfx::memory::Usage::Dynamic, + (0, 0), + gfx::format::Swizzle::new(), + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + )?; + /* renderer.flush(); + renderer.update_texture( + &texture, + [0, 0], + [max_texture_size, max_texture_size], + &vec![[0u8; 4]; (usize::from(max_texture_size) * usize::from(max_texture_size))], + //&[[255u8; 4]; 64 * 64], + // NOTE: Cast is safe since the origin was a u16. + )?; + renderer.flush(); */ + // texture.cleanup(); + // Not sure if this is necessary... + // renderer.flush(); + // texture.update(); + // // FIXME: Currently, there seems to be a bug where the very first texture + // update always // fails. Not sure why, but we currently work around + // it with a dummy allocation (which we // proceed to leak, in case the + // bug can return after it's freed). let _ = atlas.allocate(guillotiere: + // :Size::new(64, 64)); + Ok((atlas, texture)) } } -pub struct FigureState { +pub struct FigureStateMeta { bone_consts: Consts, locals: Consts, lantern_offset: Vec3, state_time: f64, - skeleton: S, last_ori: Vec3, lpindex: u8, visible: bool, } +pub struct FigureState { + meta: FigureStateMeta, + skeleton: S, +} + +impl Deref for FigureState { + type Target = FigureStateMeta; + + fn deref(&self) -> &Self::Target { &self.meta } +} + +impl DerefMut for FigureState { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.meta } +} + impl FigureState { pub fn new(renderer: &mut Renderer, skeleton: S) -> Self { - let (bone_consts, lantern_offset) = skeleton.compute_matrices(); + let (bone_consts, lantern_offset) = skeleton + .compute_matrices(|mat| FigureBoneData::new(mat, mat.map_cols(|c| c.normalized()))); Self { - bone_consts: renderer.create_consts(&bone_consts).unwrap(), - locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), - lantern_offset, - state_time: 0.0, + meta: FigureStateMeta { + bone_consts: renderer.create_consts(&bone_consts).unwrap(), + locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), + lantern_offset, + state_time: 0.0, + last_ori: Vec3::zero(), + lpindex: 0, + visible: false, + }, skeleton, - last_ori: Vec3::zero(), - lpindex: 0, - visible: false, } } @@ -2061,31 +2288,59 @@ impl FigureState { col: Rgba, dt: f32, state_animation_rate: f32, - lpindex: u8, - visible: bool, + model: &FigureModel, + _lpindex: u8, + _visible: bool, is_player: bool, + camera: &Camera, ) { - self.visible = visible; + let _frustum = camera.frustum(); + + // Approximate as a sphere with radius equal to the + // largest dimension (if we were exact, it should just be half the largest + // dimension, but we're not, so we double it and use size() instead of + // half_size()). + let radius = model.bounds.half_size().reduce_partial_max(); + let _bounds = BoundingSphere::new(pos.into_array(), scale * 0.8 * radius); + + /* let (in_frustum, lpindex) = bounds.coherent_test_against_frustum(frustum, self.lpindex); + let visible = visible && in_frustum; + self.lpindex = lpindex; + self.visible = visible; */ // What is going on here? // (note: that ori is now the slerped ori) self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt); self.state_time += (dt * state_animation_rate) as f64; + let _focus_off = camera.get_focus_pos().map(|e| e.trunc()); let mat = Mat4::::identity() - * Mat4::translation_3d(pos) + // * Mat4::translation_3d(pos - focus_off) * Mat4::rotation_z(-ori.x.atan2(ori.y)) * Mat4::rotation_x(ori.z.atan2(Vec2::from(ori).magnitude())) * Mat4::scaling_3d(Vec3::from(0.8 * scale)); - let locals = FigureLocals::new(mat, col, is_player); + /* let dependents = camera.get_dependents(); + let all_mat = dependents.proj_mat * dependents.view_mat; */ + + let atlas_offs = model.allocation.rectangle.min; + let locals = FigureLocals::new( + mat, + col, + pos, + Vec2::new(atlas_offs.x, atlas_offs.y), + is_player, + ); renderer.update_consts(&mut self.locals, &[locals]).unwrap(); - let (new_bone_consts, lantern_offset) = self.skeleton.compute_matrices(); + let (new_bone_consts, lantern_offset) = self.skeleton.compute_matrices(|bone_mat| { + let model_mat = mat * bone_mat; + FigureBoneData::new(model_mat, model_mat.map_cols(|c| c.normalized())) + }); renderer .update_consts( - &mut self.bone_consts, + &mut self.meta.bone_consts, &new_bone_consts[0..self.skeleton.bone_count()], ) .unwrap(); diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index bb1c65f86c..e968a4222f 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -14,7 +14,7 @@ use crate::{ anim::character::SkeletonAttr, audio::{music::MusicMgr, sfx::SfxMgr, AudioFrontend}, render::{ - create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals, + self, create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline, }, settings::Settings, @@ -27,18 +27,23 @@ use common::{ terrain::{BlockKind, TerrainChunk}, vol::ReadVol, }; +use core::{iter, mem}; +use hashbrown::HashMap; +use num::traits::{Float, FloatConst}; use specs::{Entity as EcsEntity, Join, WorldExt}; use vek::*; // TODO: Don't hard-code this. const CURSOR_PAN_SCALE: f32 = 0.005; -const MAX_LIGHT_COUNT: usize = 32; +const MAX_LIGHT_COUNT: usize = 31; const MAX_SHADOW_COUNT: usize = 24; -const NUM_DIRECTED_LIGHTS: usize = 2; +const NUM_DIRECTED_LIGHTS: usize = 1; const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not emit light from their origin const SHADOW_DIST_RADIUS: f32 = 8.0; const SHADOW_MAX_DIST: f32 = 96.0; // The distance beyond which shadows may not be visible +/// The minimum sin γ we will use before switching to uniform mapping. +const EPSILON_GAMMA: f64 = 0.25; // const NEAR_PLANE: f32 = 0.5; // const FAR_PLANE: f32 = 100000.0; @@ -78,6 +83,7 @@ pub struct Scene { /// chunk. map_bounds: Vec2, select_pos: Option>, + light_data: Vec, figure_mgr: FigureMgr, sfx_mgr: SfxMgr, @@ -98,6 +104,749 @@ pub struct SceneData<'a> { pub is_aiming: bool, } +impl<'a> SceneData<'a> { + pub fn get_sun_dir(&self) -> Vec3 { Globals::get_sun_dir(self.state.get_time_of_day()) } + + pub fn get_moon_dir(&self) -> Vec3 { Globals::get_moon_dir(self.state.get_time_of_day()) } +} + +pub fn aabb_to_points(bounds: Aabb) -> [Vec3; 8] { + [ + Vec3::new(bounds.min.x, bounds.min.y, bounds.min.z), + Vec3::new(bounds.max.x, bounds.min.y, bounds.min.z), + Vec3::new(bounds.max.x, bounds.max.y, bounds.min.z), + Vec3::new(bounds.min.x, bounds.max.y, bounds.min.z), + Vec3::new(bounds.min.x, bounds.min.y, bounds.max.z), + Vec3::new(bounds.max.x, bounds.min.y, bounds.max.z), + Vec3::new(bounds.max.x, bounds.max.y, bounds.max.z), + Vec3::new(bounds.min.x, bounds.max.y, bounds.max.z), + ] +} + +/// Each Vec4 should be interpreted as reprenting plane equation +/// +/// a(x - x0) + b(y - y0) + c(z - z0) = 0, i.e. +/// ax + by + cz - (a * x0 + b * y0 + c * z0) = 0, i.e. +/// ax + by + cz = (a * x0 + b * y0 + c * z0), i.e. +/// (lettiing d = a * x0 + b * y0 + c * z0) +/// ax + by + cz = d +/// +/// where d is the distance of the plane from the origin. +pub fn aabb_to_planes(bounds: Aabb) -> [(Vec3, T); 6] { + let zero = T::zero(); + let one = T::one(); + let bounds = bounds.map(|e| e.abs()); + [ + // bottom + (Vec3::new(zero, -one, zero), bounds.min.y), + // top + (Vec3::new(zero, one, zero), bounds.max.y), + // left + (Vec3::new(-one, zero, zero), bounds.min.x), + // right + (Vec3::new(one, zero, zero), bounds.max.x), + // near + (Vec3::new(zero, zero, -one), bounds.min.z), + // far + (Vec3::new(zero, zero, one), bounds.max.z), + ] +} + +pub fn mat_mul_points>( + mat: Mat4, + pts: &mut [Vec3], + mut do_p: impl FnMut(Vec4) -> Vec3, +) { + pts.into_iter().for_each(|p| { + *p = do_p(mat * Vec4::from_point(*p)); + }); +} + +/// NOTE: Expects points computed from aabb_to_points. +pub fn calc_view_frust_object(pts: &[Vec3; 8]) -> Vec>> { + vec![ + // near (CCW) + vec![pts[0], pts[1], pts[2], pts[3]], + // far (CCW) + vec![pts[7], pts[6], pts[5], pts[4]], + // left (CCW) + vec![pts[0], pts[3], pts[7], pts[4]], + // right (CCW) + vec![pts[1], pts[5], pts[6], pts[2]], + // bottom (CCW) + vec![pts[4], pts[5], pts[1], pts[0]], + // top (CCW) + vec![pts[6], pts[7], pts[3], pts[2]], + ] +} + +pub fn calc_view_frustum_world_coord>( + inv_proj_view: Mat4, +) -> [Vec3; 8] { + let mut world_pts = aabb_to_points(Aabb { + min: -Vec3::one(), + max: Vec3::one(), + }); + mat_mul_points(inv_proj_view, &mut world_pts, |p| Vec3::from(p) / p.w); + world_pts +} + +pub fn point_plane_distance(point: Vec3, (norm, dist): (Vec3, T)) -> T { + norm.dot(point) - dist +} + +pub fn point_before_plane(point: Vec3, plane: (Vec3, T)) -> bool { + point_plane_distance(point, plane) > T::zero() +} + +/// Returns true if and only if the final point in the polygon (i.e. the first +/// point added to the new polygon) is outside the clipping plane (this implies +/// that the polygon must be non-degenerate). +pub fn clip_points_by_plane + core::fmt::Debug>( + points: &mut Vec>, + plane: (Vec3, T), + intersection_points: &mut Vec>, +) -> bool { + /* enum Intersection { + /// Previous point was inside the plane. + Inside, + /// Previous line segment was completely outside the plane. + Outside, + /// Previous line segment went from inside the plane to outside it. + InsideOut, + } */ + // println!("points@clip_points_by_plane before clipping by {:?}: {:?}", plane, + // points); + if points.len() < 3 { + return false; + } + // NOTE: Guaranteed to succeed since points.len() > 3. + let mut current_point = points[points.len() - 1]; + let (norm, dist) = plane; + let intersect_plane_edge = |a, b| { + let diff = b - a; + let t = norm.dot(diff); + if t == T::zero() { + None + } else { + let t = (dist - norm.dot(a)) / t; + if t < T::zero() || T::one() < t { + None + } else { + Some(diff.mul_add(Vec3::broadcast(t), a)) + } + } + }; + let last_is_outside = point_before_plane(current_point, plane); + let mut is_outside = last_is_outside; + /* // Might not actually be total, but if it is partial and the point is inside it will be + // written regardless, and if it is partial and the point is outside, it means the + // second-to-last point is inside; thus, the second-to-last point will be written regardless, + // current_point will hold the new intersection point, and is_total will be false, when the + // loop ends; thus all we need to do to take this case into account is to push current_point + // onto the points vector if (is_total || is_outside) is false at the end of the loop. + let mut is_total = true; */ + let mut old_points = Vec::with_capacity((3 * points.len()) / 2); + mem::swap(&mut old_points, points); + old_points.into_iter().for_each(|point| { + /* let prev_point = current_point; + // Swap point i with the previous point in the polygon, so it is the one we normally save + // when we return false. + mem::swap(&mut current_point, point); */ + let prev_point = mem::replace(&mut current_point, point); + /* if point_before_plane(current_point) { + // If we are an outside point, we should only calculate an intersection if the previous + // point was inside. + if + is_outside s + // point was outside. + } else { + // If we are an inside point, then we should only calculate an intersection if the previous + // point was outside. + } */ + let before_plane = point_before_plane(current_point, plane); + let prev_is_outside = mem::replace(&mut is_outside, before_plane); + // println!("points@clip_points_by_plane clipping segment by {:?} (prev={:?} / + // outside={:?}, current={:?} / outside={:?})", plane, prev_point, + // prev_is_outside, current_point, is_outside); + if !prev_is_outside { + // Push previous point. + points.push(prev_point); + } + if prev_is_outside != is_outside { + if let Some(intersection_point) = intersect_plane_edge(prev_point, current_point) { + // Push intersection point. + intersection_points.push(intersection_point); + points.push(intersection_point); + } + } + /* let prev_is_total = mem::replace( + &mut is_total, + // Save the intersection point only if we go from outside to inside or inside to + // outside, and definitely intersect the plane edge. + prev_is_outside != is_outside && + + .map(|intersection_point| { + intersection_points.push(intersection_point); + if prev_is_outside { + // If the previous point is outside, we know + *point = intersection_point; + } else { + // i o i o + // + // i o (2) + // i i/o o/i (3) + // + // i o i (3) + // i i/o o/i i (4) + // + // i o i o (4) + // i i/o o/i i i/o o/i (6) + // + // i o i o i (5) + // i i/o o/i i i/o o/i i (7) + // + // i o i o i o (6) + // i i/o o/i i i/o o/i i i/o o/i (9) + current_point = intersection_point; + } + false + }) + .is_none(), + ); + // Save the previous point if it is either inside, or has been replaced by an intersection + // point. + !prev_is_outside || prev_is_total + /* match (prev_is_outside, is_outside) { + (true, true) => { + prev_is_total + }, + (true, false) => { + // Outside to inside, so save the previous point only if it's been replaced by an + // intersection point. + do_intersection(); + prev_is_total + }, + (false, true) => { + // Inside to outside, so always save the previous point, and save the intersection + // point only if we definitively intersect the plane edge. + false + }, + (false, false) => { + // Both points inside the plane, so always save the previous point. + false + } + } */ */ + }); + /* if !(is_total || is_outside) { + points.push(current_point); + } + /* match (before_plane, is_outside) { + (true, Previous::Outside) => { + + } + } + let cur_is_outside = { + if let Intersection::Inside = is_outside { + } else { + } + let prev_is_outside = mem::replace(&mut is_outside, { + let if let Intersection::Inside = is_outside { + true + } else { + false + } point_before_plane(current_point) { + }); + match (prev_is_outside, is_outside) { + (true, Some(is_outside)) => { + // Both points outside the plane, so save the previous point only if it's been + // replaced by an intersection point. + is_outside + }, + (true, false) => { + // Outside to inside, so calculate the intersection, and save it. + intersect_points.push(*point); + false + }, + (false, true) => { + // Inside to outside, so calculate the intersection, and save it and the current + // point. + intersect_points.push(*point); + false + }, + (false, false) => { + // Both points inside the plane, so save previous point + *point = * + false + } + } + if is_outside { + if prev_is_outside { + } else { + } + } else { + if prev_is_outside { + } + } + });*/ }*/ + last_is_outside +} + +fn append_intersection_points( + polys: &mut Vec>>, + intersection_points: Vec>, + tolerance: T, +) { + // NOTE: We use decoded versions of each polygon, with rounded entries. + // + // The line segments in intersection_points are consistently ordered as follows: + // each segment represents the intersection of the cutting plane with the + // polygon from which the segment came. The polygon can thus be split into + // two parts: the part "inside" the new surface (below the plane), and the + // part "outside" it (above the plane). Thus, when oriented + // with the polygon normal pointing into the camera, and the cutting plane as + // the x axis, with the "outside" part on top and the "inside" part on the + // bottom, there is a leftmost point (the point going from outside to + // inside, counterclockwise) and a rightmost point (the point going from + // inside to outside, counterclockwise). Our consistent ordering guarantees + // that the leftmost point comes before the rightmost point in each line + // segment. + // + // Why does this help us? To see that, consider the polygon adjacent to the + // considered polygon which also has the same right intersection point (we + // know there will be exactly one of these, because we have a solid + // structure and are only considering polygons that intersect the plane + // exactly two times; this means that we are ignoring polygons that intersect + // the plane at just one point, which means the two polygons must share a + // point, not be coplanar, and both intersect the plane; no theorem here, + // but I believe there can provably be at most one such instance given that + // we have at least three polygons with such a line segment). + // + // Now, for the adjacent polygon, repeat the above process. If the intersection + // point shared by the polygons is on the right in both cases, then we can + // see that the polygon's normal must be facing in the opposite direction of + // the original polygon despite being adjacent. But this + // should be impossible for a closed object! The same applies to the leftmost + // point. + // + // What is the practical upshot of all this? It means that we can consistently + // hash each line segment by its first point, which we can look up using the + // second point of a previous line segment. This will produce a chain of + // entries terminating in the original segment we looked up. As an added + // bonus, by going from leftmost point to leftmost point, we also ensure that + // we produce a polygon whose face is oriented counterclockwise around its + // normal; this can be seen by following the right-hand rule (TODO: provide + // more rigorous proof). + let tol = tolerance.recip(); + let make_key = move |point: Vec3| { + // We use floating points rounded to tolerance in order to make our HashMap + // lookups work. Otherwise we'd have to use a sorted structure, like a + // btree, which wouldn't be the end of the world but would have + // theoretically worse complexity. NOTE: Definitely non-ideal that we + // panic if the rounded value can't fit in an i64... TODO: If necessary, + // let the caller specify how to hash these keys, since in cases where + // we know the kind of floating point we're using we can just cast to bits or + // something. + point.map(|e| { + (e * tol) + .round() + .to_i64() + .expect("We don't currently try to handle floats that won't fit in an i64.") + }) + }; + let mut lines_iter = intersection_points.chunks_exact(2).filter_map(|uv| { + let u_key = make_key(uv[0]); + let v = uv[1]; + // NOTE: The reason we need to make sure this doesn't happen is that it's + // otherwise possible for two points to hash to the same value due to + // epsilon being too low. Because of the ordering mentioned previously, + // we know we should *eventually* find a pair of points starting with + // make_key(u) and ending with a different make_key(v) in such cases, so + // we just discard all the other pairs (treating them as points rather + // than lines). + (u_key != make_key(v)).then_some((u_key, v)) + }); + // .map(|uv| (make_key(uv[0]), uv[1])) + + if let Some((last_key, first)) = lines_iter.next() + /* [last, first, rest @ ..] = &*intersection_points = &*intersection_points */ + { + let lines = lines_iter.collect::>(); + /* if rest.len() < 4 { + // You need at least 3 sides for a polygon + return; + } + let lines = rest + .chunks_exact(2) + .filter_map(|uv| { + let u_key = make_key(uv[0]); + let v = uv[1]; + (u_key != make_key(v)).then_some((u_key, v)) + }) + // .map(|uv| (make_key(uv[0]), uv[1])) + .collect::>(); */ + if lines.len() < 2 { + // You need at least 3 sides for a polygon + return; + } + // println!("lines@append_intersection_points before merging points (last={:?}, + // cur={:?}): {:?}", last, cur, lines); + // let mut poly = Vec::with_capacity(lines.len() + 1); + // poly.push(first); + // NOTE: Guaranteed to terminate, provided we have no cycles besides the one + // that touches every point (which should be the case given how these + // points were generated). + let /*mut */poly_iter = iter::successors(Some(first), |&cur| lines.get(&make_key(cur)).copied()); + /* poly.extend(poly_iter.next()); + // TODO: If we were smart and pre-tested whether (last, first) was a dup (guaranteeing we + // started on a non-dup), we would not need the take_while part. + poly.extend(poly_iter.take_while(|&cur| make_key(cur) != make_key(first))); + /* while let Some(&v) = lines.get(&make_key(cur)) { + cur = v; + poly.push(cur); + } */ */ + let poly: Vec<_> = poly_iter.collect(); + // We have to check to make sure we really went through the whole cycle. + // TODO: Consider adaptively decreasing precision until we can make the cycle + // happen. + if poly.last().copied().map(make_key) == Some(last_key) { + // Push the new polygon onto the object. + polys.push(poly); + } + } +} + +pub fn clip_object_by_plane + core::fmt::Debug>( + polys: &mut Vec>>, + plane: (Vec3, T), + tolerance: T, +) { + let mut intersection_points = Vec::new(); + polys.drain_filter(|points| { + let len = intersection_points.len(); + let outside_first = clip_points_by_plane(points, plane, &mut intersection_points); + // println!("points@clip_object_by_plane after clipping by {:?} (outside_first={:?}, intersection_points={:?}): {:?}", plane, outside_first, intersection_points, points); + // Only remember intersections that are not coplanar with this side; i.e. those + // that have segment length 2. + if len + 2 != intersection_points.len() { + intersection_points.truncate(len); + } else if !outside_first { + // Order the two intersection points consistently, so that, when considered + // counterclockwise: + // - the first point goes from the exterior of the polygon (above the cutting + // plane) to its interior. + // - the second point goes from the interior of the polygon (below the cutting + // plane) to its exterior. + // the second is always going + // + // This allows us to uniquely map each line segment to an "owning" point (the + // one going from outside to inside), which happens to also point + // the segment in a counterclockwise direction around the new + // polygon normal composed of all the lines we clipped. + intersection_points.swap(len, len + 1); + } + // Remove polygon if it was clipped away + points.is_empty() + }); + // println!("polys@clip_object_by_plane after clipping by {:?} (before appending + // interection points {:?}): {:?}", plane, intersection_points, polys); + // Add a polygon of all intersection points with the plane to close out the + // object. + append_intersection_points(polys, intersection_points, tolerance); +} + +pub fn clip_object_by_aabb + core::fmt::Debug>( + polys: &mut Vec>>, + bounds: Aabb, + tolerance: T, +) { + let planes = aabb_to_planes(bounds); + // println!("planes@clip_object_by_aabb: {:?}", planes); + planes.iter().for_each(|&plane| { + clip_object_by_plane(polys, plane, tolerance); + // println!("polys@clip_object_by_aabb (after clipping by {:?}): {:?}", + // plane, polys); + }); +} + +/// Return value is 'Some(segment)' if line segment intersects the current test +/// plane. Otherwise 'None' is returned in which case the line segment +/// is entirely clipped. +pub fn clip_test(p: T, q: T, (u1, u2): (T, T)) -> Option<(T, T)> { + /* let res = */ + if p == T::zero() { + if q >= T::zero() { Some((u1, u2)) } else { None } + } else { + let r = q / p; + if p < T::zero() { + if r > u2 { + None + } else { + Some((if r > u1 { r } else { u1 }, u2)) + } + } else { + if r < u1 { + None + } else { + Some((u1, if r < u2 { r } else { u2 })) + } + } + } /*; + // println!("clip_test@(p={:?}, q={:?}, (u1, u2)=({:?}. {:?})): res={:?}", + // p, q, u1, u2, res); res*/ +} + +pub fn intersection_line_aabb + core::fmt::Debug>( + p: Vec3, + dir: Vec3, + bounds: Aabb, +) -> Option> { + // println!("before@intersection_line_aabb: p={:?} dir={:?} bounds={:?}", p, + // dir, bounds); + /* let res = */ + clip_test(-dir.z, p.z - bounds.min.z, (T::zero(), T::infinity())) + .and_then(|t| clip_test(dir.z, bounds.max.z - p.z, t)) + .and_then(|t| clip_test(-dir.y, p.y - bounds.min.y, t)) + .and_then(|t| clip_test(dir.y, bounds.max.y - p.y, t)) + .and_then(|t| clip_test(-dir.x, p.x - bounds.min.x, t)) + .and_then(|t| clip_test(dir.x, bounds.max.x - p.x, t)) + .and_then(|(t1, t2)| { + if T::zero() <= t2 { + Some(dir.mul_add(Vec3::broadcast(t2), p)) + } else if T::zero() <= t1 { + Some(dir.mul_add(Vec3::broadcast(t1), p)) + } else { + None + } + }) /*; + //println!("after@intersection_line_aabb (p={:?} dir={:?} bounds={:?}): + // {:?}", p, dir, bounds, res); res */ +} + +pub fn include_object_light_volume< + T: Float + MulAdd + core::fmt::Debug, + I: Iterator>, +>( + obj: I, + light_dir: Vec3, + bounds: Aabb, +) -> impl Iterator> { + /* obj.filter_map(move |pt| intersection_line_aabb(pt, -light_dir, bounds)) */ + // obj.map(move |pt| intersection_line_aabb(pt, -light_dir, + // bounds).unwrap_or(pt)) + obj.flat_map(move |pt| iter::once(pt).chain(intersection_line_aabb(pt, -light_dir, bounds))) +} + +pub fn calc_focused_light_volume_points + core::fmt::Debug>( + inv_proj_view: Mat4, + _light_dir: Vec3, + scene_bounding_box: Aabb, + tolerance: T, +) -> impl Iterator> { + let world_pts = calc_view_frustum_world_coord(inv_proj_view); + // println!("world_pts: {:?}", world_pts); + let mut world_frust_object = calc_view_frust_object(&world_pts); + // println!("world_frust_object: {:?}", world_frust_object); + clip_object_by_aabb(&mut world_frust_object, scene_bounding_box, tolerance); + // println!("world_frust_object@clip_object_by_aabb: {:?}", world_frust_object); + /* let object_points = world_frust_object.into_iter().flat_map(|e| e.into_iter()); + object_points.clone().chain(include_object_light_volume(object_points, light_dir, scene_bounding_box)) */ + world_frust_object.into_iter().flat_map(|e| e.into_iter()) + /* include_object_light_volume( + world_frust_object.into_iter().flat_map(|e| e.into_iter()), + light_dir, + scene_bounding_box, + ) */ +} + +/// Approximte a scalar field of view angle using the parameterization from +/// section 4.3 of Lloyd's thesis: +/// +/// W_e = 2 n_e tan θ +/// +/// where +/// +/// W_e = 2 is the width of the image plane (for our projections, since they go +/// from -1 to 1) n_e = near_plane is the near plane for the view frustum +/// θ = (fov / 2) is the half-angle of the FOV (the one passed to +/// Mat4::projection_rh_no). +/// +/// Although the widths for the x and y image planes are the same, they are +/// different in this framework due to the introduction of an aspect ratio: +/// +/// y'(p) = 1.0 / tan(fov / 2) * p.y / -p.z +/// x'(p) = 1.0 / (aspect * tan(fov / 2)) * p.x / -p.z +/// +/// i.e. +/// +/// y'(x, y, -near, w) = 1 / tan(fov / 2) p.y / near +/// x'(x, y, -near, w) = 1 / (aspect * tan(fov / 2)) p.x / near +/// +/// W_e,y = 2 * near_plane * tan(fov / 2) +/// W_e,x = 2 * near_plane * aspect * W_e,y +/// +/// Θ_x = atan(W_e_y / 2 / near_plane) = atanfov / t() +/// +/// i.e. we have an "effective" W_e_x of +/// +/// 2 = 2 * near_plane * tan Θ +/// +/// atan(1 / near_plane) = θ +/// +/// y' +/// x(-near) +/// W_e = 2 * near_plane * +/// +/// W_e_y / n_e = tan (fov / 2) +/// W_e_x = 2 n +fn compute_scalar_fov(_near_plane: F, fov: F, aspect: F) -> F { + let two = F::one() + F::one(); + let theta_y = fov / two; + let theta_x = (aspect * theta_y.tan()).atan(); + /* let h = (fov / two).tan().recip(); + let w = h / aspect; + let theta_y = (h / two).atan(); + let theta_x = (w / two).atan(); */ + /* // let theta_x = ((aspect * (fov / two).tan()).recip()/* / (two * near_plane)*/).atan(); + // let theta_y = ((fov / two).tan().recip()/* / (two * near_plane)*/).atan(); + let theta_x = ((aspect * (fov / two).tan()) / ).atan(); + let theta_y = ((fov / two).tan().recip()/* / (two * near_plane)*/).atan(); */ + theta_x.min(theta_y) + // near_plane.recip().atan() + /* fov / two */ +} + +/// Compute a near-optimal warping parameter that helps minimize error in a +/// shadow map. +/// +/// See section 5.2 of Brandon Lloyd's thesis: +/// +/// [http://gamma.cs.unc.edu/papers/documents/dissertations/lloyd07.pdf](Logarithmic Perspective Shadow Maps). +/// +/// η = +/// 0 γ < γ_a +/// -1 + (η_b + 1)(1 + cos(90 (γ - γ_a)/(γ_b - γ_a))) γ_a ≤ γ < γ_b +/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c +/// η_c γ_c ≤ γ +fn compute_warping_parameter( + gamma: F, + (gamma_a, gamma_b, gamma_c): (F, F, F), + (eta_b, eta_c): (F, F), +) -> F { + if gamma < gamma_a { + F::zero() + } else if gamma_a <= gamma && gamma < gamma_b { + -F::one() + (eta_b + F::one()) * (F::one() + (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos()) + } else if gamma_b <= gamma && gamma < gamma_c { + eta_b + (eta_c - eta_b) * (F::FRAC_PI_2() * (gamma - gamma_b) / (gamma_c - gamma_b)).sin() + } else { + eta_c + } + // NOTE: Just in case we go out of range due to floating point imprecision. + .max(-F::one()).min(F::one()) +} + +/// Compute a near-optimal warping parameter that falls off quickly enough +/// when the warp angle goes past the minimum field of view angle, for +/// perspective projections. +/// +/// For F_p (perspective warping) and view fov angle θ,the parameters are: +/// +/// γ_a = θ / 3 +/// γ_b = θ +/// γ_c = θ + 0.3(90 - θ) +/// +/// η_b = -0.2 +/// η_c = 0 +/// +/// See compute_warping_parameter. +fn compute_warping_parameter_perspective( + gamma: F, + near_plane: F, + fov: F, + aspect: F, +) -> F { + let theta = compute_scalar_fov(near_plane, fov, aspect); + let two = F::one() + F::one(); + let three = two + F::one(); + let ten = three + three + three + F::one(); + compute_warping_parameter( + gamma, + ( + theta / three, + theta, + theta + (three / ten) * (F::FRAC_PI_2() - theta), + ), + (-two / ten, F::zero()), + ) +} + +/// NOTE: Will not yield useful results if pts is empty! +pub fn fit_psr< + T: Float + MulAdd, + I: Iterator>, + F: FnMut(Vec4) -> Vec3, +>( + mat: Mat4, + pts: I, + mut do_p: F, +) -> Aabb { + let mut min = Vec3::broadcast(T::infinity()); + let mut max = Vec3::broadcast(T::neg_infinity()); + pts.map(|p| do_p(mat * Vec4::::from_point(p))) + .for_each(|p| { + min = Vec3::partial_min(min, p); + max = Vec3::partial_max(max, p); + }); + Aabb { min, max } + /* let mut make_p = |x: f32, y: f32, z: f32| -> Vec3 { + do_p(mat * Vec4::new(x, y, z, 1.0)) + }; + let p1 = make_p(bounds.min.x, bounds.min.y, bounds.min.z); + let p2 = make_p(bounds.max.x, bounds.min.y, bounds.min.z); + let p3 = make_p(bounds.min.x, bounds.max.y, bounds.min.z); + let p4 = make_p(bounds.max.x, bounds.max.y, bounds.min.z); + let p5 = make_p(bounds.min.x, bounds.min.y, bounds.max.z); + let p6 = make_p(bounds.max.x, bounds.min.y, bounds.max.z); + let p7 = make_p(bounds.min.x, bounds.max.y, bounds.max.z); + let p8 = make_p(bounds.max.x, bounds.max.y, bounds.max.z); + // let p1: Vec4 = mat * Vec4::new(bounds.min.x, bounds.min.y, bounds.min.z, 1.0); + // let p2: Vec4 = mat * Vec4::new(0.0, bounds.min.y, 0.0, 1.0); + // let p3: Vec4 = mat * Vec4::new(0.0, 0.0, bounds.min.z, 1.0); + // let p4: Vec4 = mat * Vec4::new(bounds.max.x, 0.0, 0.0, 1.0); + // let p5: Vec4 = mat * Vec4::new(0.0, bounds.max.y, 0.0, 1.0); + // let p6: Vec4 = mat * Vec4::new(bounds.max.x, bounds.max.y, bounds.max.z, 1.0); + // println!("p1 p6 {:?} {:?}", p1, p6); + // let xmin = p1.x.min(p6.x); + // let xmax = p1.x.max(p6.x); + // println!("p1 p2 p3 p4 p5 p6: {:?} {:?} {:?} {:?} {:?} {:?}", p1, p2, p3, p4, p5, p6); + let xmin = p1.x.min(p2.x.min(p3.x.min(p4.x.min(p5.x.min(p6.x.min(p7.x.min(p8.x))))))); + let xmax = p1.x.max(p2.x.max(p3.x.max(p4.x.max(p5.x.max(p6.x.max(p7.x.max(p8.x))))))); + // let xmin = p1.x.min(p2.x.min(p3.x.min(p4.x.min(p5.x.min(p6.x))))); + // let xmax = p1.x.max(p2.x.max(p3.x.max(p4.x.max(p5.x.max(p6.x))))); + // println!("xmin: {:?}, xmax: {:?}", xmin, xmax); + // let ymin = p1.y.min(p6.y); + // let ymax = p1.y.max(p6.y); + let ymin = p1.y.min(p2.y.min(p3.y.min(p4.y.min(p5.y.min(p6.y.min(p7.y.min(p8.y))))))); + let ymax = p1.y.max(p2.y.max(p3.y.max(p4.y.max(p5.y.max(p6.y.max(p7.y.max(p8.y))))))); + // println!("ymin: {:?}, ymax: {:?}", ymin, ymax); + + // let p1: Vec4 = view_mat * Vec4::new(scene_bounds.min.x, scene_bounds.min.y, scene_bounds.min.z, 1.0); + // let p2: Vec4 = view_mat * Vec4::new(0.0, scene_bounds.min.y, 0.0, 1.0); + // let p3: Vec4 = view_mat * Vec4::new(0.0, 0.0, scene_bounds.min.z, 1.0); + // let p4: Vec4 = view_mat * Vec4::new(scene_bounds.max.x, scene_bounds.max.y, scene_bounds.max.z, 1.0); + // let p5: Vec4 = view_mat * Vec4::new(0.0, scene_bounds.max.y, 0.0, 1.0); + // let p6: Vec4 = view_mat * Vec4::new(0.0, 0.0, scene_bounds.max.z, 1.0); + // println!("p1 p2 p3 p4 p5 p6: {:?} {:?} {:?} {:?} {:?} {:?}", p1, p2, p3, p4, p5, p6); + // println!("p1 p4 {:?} {:?}", p1, p4); + let zmin = p1.z.min(p2.z.min(p3.z.min(p4.z.min(p5.z.min(p6.z.min(p7.z.min(p8.z))))))); + let zmax = p1.z.max(p2.z.max(p3.z.max(p4.z.max(p5.z.max(p6.z.max(p7.z.max(p8.z))))))); + Aabb { + min: Vec3::new(xmin, ymin, zmin), + max: Vec3::new(xmax, ymax, zmax), + } */ +} + impl Scene { /// Create a new `Scene` with default parameters. pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self { @@ -112,7 +861,7 @@ impl Scene { .create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]) .unwrap(), shadow_mats: renderer - .create_consts(&[ShadowLocals::default(); MAX_LIGHT_COUNT * 6 + 2]) + .create_consts(&[ShadowLocals::default(); MAX_LIGHT_COUNT * 6 + 6]) .unwrap(), camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson), camera_input_state: Vec2::zero(), @@ -132,8 +881,9 @@ impl Scene { loaded_distance: 0.0, map_bounds: client.world_map.2, select_pos: None, + light_data: Vec::new(), - figure_mgr: FigureMgr::new(), + figure_mgr: FigureMgr::new(renderer), sfx_mgr: SfxMgr::new(), music_mgr: MusicMgr::new(), } @@ -282,46 +1032,49 @@ impl Scene { } = self.camera.dependents(); // Update chunk loaded distance smoothly for nice shader fog - self.loaded_distance = + let loaded_distance = (0.98 * self.loaded_distance + 0.02 * scene_data.loaded_distance).max(0.01); // Update light constants - let mut lights = ( - &scene_data.state.ecs().read_storage::(), - scene_data.state.ecs().read_storage::().maybe(), - scene_data - .state - .ecs() - .read_storage::() - .maybe(), - &scene_data - .state - .ecs() - .read_storage::(), - ) - .join() - .filter(|(pos, _, _, _)| { - (pos.0.distance_squared(player_pos) as f32) - < self.loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS - }) - .map(|(pos, ori, interpolated, light_anim)| { - // Use interpolated values if they are available - let (pos, ori) = - interpolated.map_or((pos.0, ori.map(|o| o.0)), |i| (i.pos, Some(i.ori))); - let rot = { - if let Some(o) = ori { - Mat3::rotation_z(-o.x.atan2(o.y)) - } else { - Mat3::identity() - } - }; - Light::new( - pos + (rot * light_anim.offset), - light_anim.col, - light_anim.strength, - ) - }) - .collect::>(); + let lights = &mut self.light_data; + lights.clear(); + lights.extend( + ( + &scene_data.state.ecs().read_storage::(), + scene_data.state.ecs().read_storage::().maybe(), + scene_data + .state + .ecs() + .read_storage::() + .maybe(), + &scene_data + .state + .ecs() + .read_storage::(), + ) + .join() + .filter(|(pos, _, _, _)| { + (pos.0.distance_squared(player_pos) as f32) + < loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS + }) + .map(|(pos, ori, interpolated, light_anim)| { + // Use interpolated values if they are available + let (pos, ori) = + interpolated.map_or((pos.0, ori.map(|o| o.0)), |i| (i.pos, Some(i.ori))); + let rot = { + if let Some(o) = ori { + Mat3::rotation_z(-o.x.atan2(o.y)) + } else { + Mat3::identity() + } + }; + Light::new( + pos + (rot * light_anim.offset), + light_anim.col, + light_anim.strength, + ) + }), + ); lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32); lights.truncate(MAX_LIGHT_COUNT); renderer @@ -344,7 +1097,7 @@ impl Scene { .filter(|(_, _, _, _, stats)| !stats.is_dead) .filter(|(pos, _, _, _, _)| { (pos.0.distance_squared(player_pos) as f32) - < (self.loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0) + < (loaded_distance.min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS).powf(2.0) }) .map(|(pos, interpolated, scale, _, _)| { Shadow::new( @@ -360,71 +1113,13 @@ impl Scene { .update_consts(&mut self.shadows, &shadows) .expect("Failed to update light constants"); + // Remember to put the new loaded distance back in the scene. + self.loaded_distance = loaded_distance; + // Update light projection matrices for the shadow map. let time_of_day = scene_data.state.get_time_of_day(); - let shadow_res = renderer.get_shadow_resolution(); - // NOTE: The aspect ratio is currently always 1 for our cube maps, since they - // are equal on all sides. - let shadow_aspect = shadow_res.x as f32 / shadow_res.y as f32; - // First, add a projected matrix for our directed hard lights. - let directed_view_proj = Mat4::orthographic_rh_no(FrustumPlanes { - left: -(shadow_res.x as f32) / 2.0, - right: shadow_res.x as f32 / 2.0, - bottom: -(shadow_res.y as f32) / 2.0, - top: shadow_res.y as f32 / 2.0, - near: SHADOW_NEAR, - far: SHADOW_FAR, - }); - // Construct matrices to transform from world space to light space for the sun - // and moon. - const TIME_FACTOR: f32 = (std::f32::consts::PI * 2.0) / (3600.0 * 24.0); - let angle_rad = time_of_day as f32 * TIME_FACTOR; - let sun_dir = Vec3::new(angle_rad.sin(), 0.0, angle_rad.cos()); - let moon_dir = Vec3::new(-angle_rad.sin(), 0.0, angle_rad.cos() - 0.5); - let sun_view_mat = Mat4::model_look_at_rh(-sun_dir, Vec3::zero(), Vec3::up()); - let moon_view_mat = Mat4::model_look_at_rh(-moon_dir, Vec3::zero(), Vec3::up()); - // Now, construct the full projection matrices in the first two directed light - // slots. - let mut shadow_mats = Vec::with_capacity(6 * (lights.len() + 1)); - shadow_mats.push(ShadowLocals::new(directed_view_proj * sun_view_mat)); - shadow_mats.push(ShadowLocals::new(directed_view_proj * moon_view_mat)); - // This leaves us with four dummy slots, which we push as defaults. - shadow_mats.extend_from_slice(&[ShadowLocals::default(); 6 - NUM_DIRECTED_LIGHTS] as _); - // Now, we tackle point lights. - // First, create a perspective projection matrix at 90 degrees (to cover a whole - // face of the cube map we're using). - let shadow_proj = - Mat4::perspective_rh_no(90.0f32.to_radians(), shadow_aspect, SHADOW_NEAR, SHADOW_FAR); - // Next, construct the 6 orientations we'll use for the six faces, in terms of - // their (forward, up) vectors. - let orientations = [ - (Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, -1.0, 0.0)), - (Vec3::new(-1.0, 0.0, 0.0), Vec3::new(0.0, -1.0, 0.0)), - (Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 0.0, 1.0)), - (Vec3::new(0.0, -1.0, 0.0), Vec3::new(0.0, 0.0, -1.0)), - (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, -1.0, 0.0)), - (Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, -1.0, 0.0)), - ]; - // NOTE: We could create the shadow map collection at the same time as the - // lights, but then we'd have to sort them both, which wastes time. Plus, we - // want to prepend our directed lights. - shadow_mats.extend(lights.iter().flat_map(|light| { - // Now, construct the full projection matrix by making the light look at each - // cube face. - let eye = Vec3::new(light.pos[0], light.pos[1], light.pos[2]); - orientations.iter().map(move |&(forward, up)| { - ShadowLocals::new(shadow_proj * Mat4::look_at_rh(eye, eye + forward, up)) - }) - })); - /* shadow_mats.push( - Mat4::orthographic_rh_no - float near_plane = 1.0f, far_plane = 7.5f; - glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); - - ); */ - renderer - .update_consts(&mut self.shadow_mats, &shadow_mats) - .expect("Failed to update light constants"); + let focus_pos = self.camera.get_focus_pos(); + let focus_off = focus_pos.map(|e| e.trunc()); // Update global constants. renderer @@ -432,7 +1127,7 @@ impl Scene { view_mat, proj_mat, cam_pos, - self.camera.get_focus_pos(), + focus_pos, self.loaded_distance, self.lod.get_data().tgt_detail as f32, self.map_bounds, @@ -446,10 +1141,10 @@ impl Scene { scene_data .state .terrain() - .get(cam_pos.map(|e| e.floor() as i32)) + .get((cam_pos + focus_off).map(|e| e.floor() as i32)) .map(|b| b.kind()) .unwrap_or(BlockKind::Air), - self.select_pos, + self.select_pos.map(|e| e - focus_off.map(|e| e as i32)), scene_data.gamma, self.camera.get_mode(), scene_data.sprite_render_distance as f32 - 20.0, @@ -460,17 +1155,709 @@ impl Scene { self.lod.maintain(renderer, time_of_day); // Maintain the terrain. - self.terrain.maintain( + let (_scene_bounds, visible_bounds, _psc_bounds) = self.terrain.maintain( renderer, &scene_data, - self.camera.get_focus_pos(), + focus_pos, self.loaded_distance, view_mat, proj_mat, ); // Maintain the figures. - self.figure_mgr.maintain(renderer, scene_data, &self.camera); + let _figure_bounds = self.figure_mgr.maintain(renderer, scene_data, &self.camera); + + let sun_dir = scene_data.get_sun_dir(); + let is_daylight = sun_dir.z < 0.0/*0.6*/; + if renderer.render_mode().shadow == render::ShadowMode::Map + && (is_daylight || !lights.is_empty()) + { + /* // We treat the actual scene bounds as being clipped by the horizontal terrain bounds, but + // expanded to contain the z values of all NPCs. This is potentially important to make + // sure we don't clip out figures in front of the camera. + let visible_bounds = Aabb { + min: Vec3::new(visible_bounds.min.x, visible_bounds.min.y, visible_bounds.min.z.min(figure_bounds.min.z)), + max: Vec3::new(visible_bounds.max.x, visible_bounds.max.y, visible_bounds.max.z.max(figure_bounds.max.z)), + }; */ + + // let focus_frac = focus_pos.map(|e| e.fract()); + let visible_bounds = Aabb { + min: visible_bounds.min - focus_off, + max: visible_bounds.max - focus_off, + }; + let visible_bounds_fine = Aabb { + min: visible_bounds.min.map(f64::from), + max: visible_bounds.max.map(f64::from), + }; + /* let visible_bounds = fit_psr(proj_mat * view_mat, visible_bounds, |p| (Vec3::from(p) / p.w)/*.map(|e| e.clamped(-1.0, 1.0))*/); + // clip bounding box points to positions that are actually visible. + // let visible_bounds_projected: aabb = fit_psr(proj_mat * view_mat, visible_bounds); + let inverse_visible: Mat4 = (proj_mat * view_mat + // .scaled_3d(vec3::new(proj_mat[(0, 0)], proj_mat[(1, 1)], 1.0)) + ).inverted();/* Mat4::identity();*/ + let visible_bounds = fit_psr(inverse_visible, visible_bounds, |p| Vec3::from(p) / p.w); */ + // let visible_pts = aabb_to_points(visible_bounds); + /* let scene_bounds = Aabb { + min: (scene_bounds.min - focus_off), + max: (scene_bounds.max - focus_off), + }; + let scene_bounds_fine = Aabb { + min: scene_bounds.min.map(f64::from), + max: scene_bounds.max.map(f64::from), + }; */ + let inv_proj_view = (proj_mat * view_mat/* * Mat4::translation_3d(-focus_off)*/) + .map(f64::from) + .inverted(); + + let fov = self.camera.get_fov(); + let aspect_ratio = self.camera.get_aspect_ratio(); + + /* println!("view_mat: {:?}", view_mat); + println!("scene_bounds: {:?} visible_bounds: {:?}", scene_bounds, visible_bounds); */ + let view_dir = ((focus_pos.map(f32::fract)) - cam_pos).normalized(); + let (point_shadow_res, _directed_shadow_res) = renderer.get_shadow_resolution(); + // NOTE: The aspect ratio is currently always 1 for our cube maps, since they + // are equal on all sides. + let point_shadow_aspect = point_shadow_res.x as f32 / point_shadow_res.y as f32; + // Construct matrices to transform from world space to light space for the sun + // and moon. + let directed_light_dir = sun_dir; + /* let light_volume = calc_focused_light_volume_points(inv_proj_view, directed_light_dir.map(f64::from), scene_bounds_fine, 1e-3) + // .map(|e| e - focus_off) + // NOTE: Hopefully not out of bounds. + .map(|v| v.map(|e| e as f32)) + .collect::>(); + // println!("light_volume: {:?}", light_volume); */ + // let visible_light_volume = light_volume.clone(); + let visible_light_volume = calc_focused_light_volume_points(inv_proj_view, directed_light_dir.map(f64::from), visible_bounds_fine, 1e-6) + // .map(|e| e - focus_off) + // NOTE: Hopefully not out of bounds. + .map(|v| v.map(|e| e as f32)) + .collect::>(); + // println!("visible_light_volume: {:?}", visible_light_volume); + // let bounds0 = fit_psr(Mat4::identity()/* * inverse_visible*/, + // light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + /* let light_volume = calc_focused_light_volume_points(inv_proj_view, directed_light_dir.map(f64::from), Aabb { + min: visible_bounds.min.map(f64::from), + max: visible_bounds.max.map(f64::from), + }, 1e-3) + // .map(|e| e - focus_off) + // NOTE: Hopefully not out of bounds. + .map(|v| v.map(|e| e as f32)) + .collect::>(); */ + // First, add a projected matrix for our directed hard lights. + // NOTE: This can be hard, so we should probably look at techniques for + // restricting what's in the light frustum for things like sunlight + // (i.e. potential shadow receivers and potential shadow casters, as + // well as other culling). The sun position is currently scaled so + // that the focus is halfway between the near plane and far plane; + // however, there is probably a much smarter way to do this. + // NOTE: Multiplying by 1.5 as an approxmiation for √(2)/2, to make sure we + // capture all chunks. + let radius = /*loaded_distance;// *//*/*scene_bounds*/scene_bounds.half_size().reduce_partial_max() * 1.5*/0.75/*bounds0/*scene_bounds*/.half_size().reduce_partial_max()*/; + + // Optimal warping for directed lights: + // + // n_opt = 1 / sin y (z_n + √(z_n + (f - n) sin y)) + // + // where n is near plane, f is far plane, y is the tilt angle between view and + // light directon, and n_opt is the optimal near plane. + let directed_near = 1.0/*0.5*/; + let _directed_far = /*128.0*/directed_near + /*loaded_distance * 2.0*/2.0 * radius; + /* let directed_proj_mat = Mat4::orthographic_rh_no/*orthographic_without_depth_planes*/(FrustumPlanes { + // TODO: Consider adjusting resolution based on view distance. + left: -/*loaded_distance*/radius, + // left: -(directed_shadow_res.x as f32) / 2.0, + right: /*loaded_distance*/radius, + // right: directed_shadow_res.x as f32 / 2.0, + bottom: -/*loaded_distance*/radius, + // bottom: -(directed_shadow_res.y as f32) / 2.0, + top: /*loaded_distance*/radius, + // top: directed_shadow_res.y as f32 / 2.0, + // TODO: Swap fixed near and far planes for something dependent on the height of the + // current scene. + near: directed_near, + far: directed_far, + }); */ + // let directed_proj_mat = Mat4::identity(); + // We also want a way to transform and scale this matrix (* 0.5 + 0.5) in order + // to transform it correctly into texture coordinates, as well as + // OpenGL coordinates. Note that the matrix for directional light + // is *already* linear in the depth buffer. + let texture_mat = Mat4::scaling_3d(0.5f32) * Mat4::translation_3d(1.0f32); + // We need to compute these offset matrices to tranform world space coordinates + // to the translated ones we use when multiplying by the light space + // matrix; this helps avoid precision loss during the + // multiplication. + + // let moon_dir = scene_data.get_moon_dir(); + // let moon_dir = Vec3::new(-angle_rad.sin(), 0.0, angle_rad.cos() - 0.5); + // Parallel light is aimed dead at the nearest integer to our focus position; if + // we were to offset by focus_off, it would be *at* our focus + // position, but using zero may result in less precision loss + // overall. NOTE: We could also try to use the offset of the + // *camera* position from the focus spot, to make shadows near the + // eye as sharp as possible. NOTE: If there's precision loss during + // the matrix *calcuation*, how much could be resolved by just using + // f64 in Rust for the computation, and switching to f32 afterwards + // just for the GPU? + // let look_at = bounds0.center();//Vec3::zero();// + // scene_bounds.center();//Vec3::zero(); let look_at = + // bounds0.center(); + let look_at = cam_pos; // /*Vec3::zero()*/scene_bounds.center()/*cam_pos*/;// - focus_off;// focus_off; + let _light_scale = 1.5 * /*(directed_near + directed_far) / 2.0*/radius; + // We upload view matrices as well, to assist in linearizing vertex positions. + // (only for directional lights, so far). + let mut directed_shadow_mats = Vec::with_capacity(6); + let new_dir = view_dir; + // let new_dir: Vec3 = light_volume/*visible_light_volume*/.iter().map(|p| + // p - cam_pos).sum(); + let new_dir = new_dir.normalized(); + /* let dot_prod = f64::from(directed_light_dir.dot(new_dir)); + let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt(); + // let sin_gamma = 0.0; + let new_dir = if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 { + new_dir + } else { + // For uniform mapping, align shadow map t axis with viewer's y axis to maximize + // utilization of the shadow map. + Vec3::from(view_mat * Vec4::from_direction(Vec3::up())) + .normalized() + }; */ + let up: Vec3 = { + /* (directed_light_dir) + .cross(new_dir) + .cross(directed_light_dir) + .normalized() */ + Vec3::up() + }; + // let up = Vec3::up(); + // let up: Vec3 = Vec3::from(Mat4::::look_at_rh(look_at - sun_dir, + // look_at, -Vec3::from(view_dir)) * Vec4::::forward_rh()); + // println!("bounds0: {:?}, scene_bounds: {:?}", bounds0, scene_bounds); + directed_shadow_mats.push(Mat4::look_at_rh( + look_at, + look_at + directed_light_dir, + /* Vec3::up()*//*Vec3::from(view_dir)*//*up*//*Vec3::down() */ up, + )); + // directed_shadow_mats.push(Mat4::look_at_rh(look_at - sun_dir * light_scale, + // look_at, /*Vec3::up()*//*Vec3::from(view_dir)*//*up*//*Vec3::down()*/up)); + // directed_shadow_mats.push(Mat4::look_at_rh(look_at - moon_dir * light_scale, + // look_at, Vec3::up())); This leaves us with four dummy slots, + // which we push as defaults. + directed_shadow_mats + .extend_from_slice(&[Mat4::default(); 6 - NUM_DIRECTED_LIGHTS] as _); + // Now, construct the full projection matrices in the first two directed light + // slots. + let mut shadow_mats = Vec::with_capacity(6 * (lights.len() + 1)); + // let cam_pos = self.camera.dependents().cam_pos - focus_off; + /* let all_mat = /*proj_mat * */view_mat + .scaled_3d(Vec3::new(proj_mat[(0, 0)], proj_mat[(1, 1)], 1.0)); + let focus_off = focus_pos.map(|e| e.trunc()); */ + let z_n = f64::from(camera::NEAR_PLANE); + let _z_f = f64::from(camera::FAR_PLANE); + let _scalar_fov = f64::from(fov / 2.0); // compute_scalar_fov(z_n, f64::from(fov), f64::from(aspect_ratio)); + shadow_mats.extend(directed_shadow_mats.iter().map(move |&light_view_mat| { + /* let visible_light_volume = { + let light_view_mat = light_view_mat.map(f64::from); + // (See http://www.songho.ca/opengl/gl_normaltransform.html) + // NOTE: What we really need here is the transpose of the matrix inverse: + // (M⁻¹)ᵀ + // + // where M is the light projection-view matrix. + // + // However, since we (hopefully) only have rotational transformations and + // transpositions for directional lights, and transpositions can be ignored by + // setting the w component of a vector to 0 (which is what we do when multiplying + // by the normal vector), we can treat M as an orthogonal matrix when multiplying + // by the normal. Thus the matrix inverse M⁻¹ can be treated as equal to its + // transpose Mᵀ, so the transpose of the inverse can be treated as equal to + // (Mᵀ)ᵀ = M for this purpose. + let inv_light_view_mat_transpose = light_view_mat; + let world_pts = calc_view_frustum_world_coord(light_view_mat * inv_proj_view); + // println!("world_pts: {:?}", world_pts); + let mut world_frust_object = calc_view_frust_object(&world_pts); + // println!("world_frust_object: {:?}", world_frust_object); + // clip_object_by_aabb(&mut world_frust_object, scene_bounding_box, tolerance); + { + let mut planes = aabb_to_planes(Aabb { + min: visible_bounds.min.map(f64::from), + max: visible_bounds.max.map(f64::from), + }); + /* let new_origin = light_view_mat * Vec4::unit_w(); + let new_origin = Vec3::from(new_origin) / new_origin.w; */ + planes.iter_mut().for_each(|plane| { + println!("old plane: {:?}", plane); + // NOTE: We may be able to simplify this to one matrix multiplication in + // this case (avoiding handling w separately) using the adjunction, but + // it's not clear whether it would be a performance win if it requires + // calculating the full matrix inverse. + let new_plane = inv_light_view_mat_transpose * Vec4::from_direction(plane.0); + /* let new_plane = light_view_mat * Vec4::new(plane.0.x, plane.0.y, plane.0.z, plane.1); */ + /* let new_plane = light_view_mat * Vec4::new(plane.0.x * plane.1, plane.0.y * plane.1, plane.0.z * plane.1, /*1.0*/0.0); */ + // We can easily find a point on the plane by multiplying the normal by the + // distance, and of course we only need to transform this point using the + // original matrix to find its new position. + let new_point = light_view_mat * Vec4::from_point(plane.0 * plane.1); + // NOTE: We currently assume no scaling, so length is 1.0. + let length: f64 = 1.0/*Vec3::from(new_plane).magnitude()*/; + let new_norm = Vec3::from(new_plane) / length; + // The new distance to the origin is the dot product of the transformed + // point on the plane's 3D coordinates, and the vector normal to the plane; + // this is because we have + // cos θ_new_point,new_norm = new_point ⋅ new_norm / (||origin|| ||new_norm||) + // = new_point ⋅ new_norm / ||origin|| + // ||origin|| cos θ_new_point,new_norm = new_point ⋅ new_norm + // which is exactly the projection of the vector from the origin to + // new_point onto the plane normal new_norm, i.e. the plane's distance + // from the origin. + *plane = (new_norm, Vec3::from(new_point).dot(new_norm)); + /* *plane = (Vec3::from(new_plane) / length, length); */ + /* let sgn = new_plane.w.signum(); + *plane = (sgn * Vec3::from(new_plane) / length, sgn * new_plane.w * length); */ + println!("new plane: {:?}", plane); + /* let new_plane = Vec3::from(light_view_mat * Vec4::from_direction(plane.0)); + *plane = (new_plane / new_plane.w, plane.1 / new_plane.w); */ + }); + // println!("planes@clip_object_by_aabb: {:?}", planes); + planes.iter().for_each(|&plane| { + clip_object_by_plane(&mut world_frust_object, plane, 1e-3); + // println!("polys@clip_object_by_aabb (after clipping by {:?}): {:?}", plane, polys); + }); + } + world_frust_object.into_iter().flat_map(|e| e.into_iter()) + .map(|v| v.map(|e| e as f32)) + .collect::>() + // + // println!("world_frust_object@clip_object_by_aabb: {:?}", world_frust_object); + // include_object_light_volume(world_frust_object.into_iter().flat_map(|e| e.into_iter()), Vec3::forward_rh(), scene_bounding_box) + }; + println!("visible_light_volume: {:?}", visible_light_volume); */ + + // let mut e_p: Vec4 = light_view_mat * Vec4::new(cam_pos.x, cam_pos.y, cam_pos.z, 1.0); + /* let mut v_p: Vec4 = /*directed_proj_mat * */light_view_mat * Vec4::from_direction(/*up*/new_dir);// Vec4::new(view_dir.x, view_dir.y, view_dir.z, 1.0); + // + // gluLookAt(e, p, y') / + // Mat4::look_at_rh(eye, target, up) / + // MathStuff::look(output, pos, dir, up) ~ Mat4::look_at_rh(pos, pos + dir, -up) + // + // eye point e = eye + // point p to look at = target + // up vector y' = up + // + // Let + // c = normalize(e - p) + // a = (y' × c) / ||y'|| = normalize(y' × c) + // b = c × a + // + // Then M_v = + // (a_x a_y a_z -(a⋅e) + // b_x b_y b_z -(b⋅e) + // c_x c_y c_z -(c⋅e) + // 0 0 0 1) + // + // c = -lightDir + // y' = -viewDir + // + // MathStuff::look(output, pos, dir, up) ~ Mat4::look_at_rh(pos, pos + dir, up): + // e = pos + // c = normalize(pos - (pos + dir)) = normalize(-dir) = -normalize(dir) = -dirN + // a = normalize(-up × c) = normalize(up × -normalize(dir)) = normalize(-(up × dir)) + // = normalize(dir × up) = lftN + // b = c × a = -normalize(dir) × lftN = normalize(-(dir × lftN)) + // = normalize(lftN × dir) = upN + // output = + // (lftN_x lftN_y lftN_z -(lftN⋅pos) + // upN_x upN_y upN_z -(upN⋅pos) + // -dirN_x -dirN_y -dirN_z dirN⋅pos + // 0 0 0 1) = + // (a_x a_y a_z -(a⋅e) + // b_x b_y b_z -(b⋅e) + // -(-c)_x -(-c)_y -(-c)_z (-c)⋅e + // 0 0 0 1) = + // (a_x a_y a_z -(a⋅e) + // b_x b_y b_z -(b⋅e) + // c_x c_y c_z -(c⋅e) + // 0 0 0 1) + // + let mut e_p: Vec3 = Vec3::zero(); + v_p.z = 0.0; */ + let mut v_p = Vec3::from(light_view_mat * Vec4::from_direction(new_dir)); + v_p.normalize(); + // let dot_prod = f64::from(v_p.z); + let dot_prod = new_dir.map(f64::from).dot(directed_light_dir.map(f64::from)); + let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt(); + let gamma = sin_gamma.asin(); + let factor = compute_warping_parameter_perspective(gamma, f64::from(camera::NEAR_PLANE), f64::from(fov), f64::from(aspect_ratio)); + /* let factor = if factor > 0.0 { + -1.0 + } else { + factor + };*/ + + v_p.z = 0.0; + v_p.normalize(); + let l_r: Mat4 = if /*v_p.magnitude_squared() > 1e-3*//*sin_gamma > EPISLON_GAMMA*/factor != -1.0 { + Mat4::look_at_rh(Vec3::zero(), Vec3::forward_rh(), v_p) + } else { + Mat4::identity() + }; + // let factor = -1.0; + // let l_r: Mat4 = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*//*Vec3::up()*/e_p, /*Vec3::from(e_p)*//*Vec3::zero()*/e_p + Vec3::forward_rh(), Vec3::from(v_p)); + // let l_r: Mat4 = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*//*Vec3::up()*/-Vec3::from(v_p), /*Vec3::from(e_p)*/Vec3::zero(), Vec3::back_rh()); + // let l_r: Mat4 = Mat4::identity();//Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*//*Vec3::up()*/-Vec3::from(v_p), /*Vec3::from(e_p)*/Vec3::zero(), Vec3::back_rh()); + // let l_r: Mat4 = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*//*Vec3::up()*/-Vec3::from(v_p), /*Vec3::from(e_p)*/Vec3::zero(), Vec3::back_rh()); + // let l_r: Mat4 = Mat4::look_at_rh(Vec3::from(e_p) - Vec3::from(v_p), Vec3::from(e_p), Vec3::forward_rh()); + // let l_r: Mat4 = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*/Vec3::zero(), /*Vec3::from(e_p)*/-Vec3::forward_rh(), /*Vec3::up()*/-Vec3::from(v_p)); + // let l_r: Mat4 = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*/Vec3::back_rh(), /*Vec3::from(e_p)*/Vec3::zero(), /*Vec3::up()*/Vec3::from(v_p)); + // let l_r: Mat4 = Mat4::identity(); + let bounds0 = fit_psr(light_view_mat, visible_light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + let directed_proj_mat = Mat4::orthographic_rh_no(FrustumPlanes { + // TODO: Consider adjusting resolution based on view distance. + left: bounds0.min.x, + right: bounds0.max.x, + bottom: bounds0.min.y, + top: bounds0.max.y, + near: bounds0.min.z, + far: bounds0.max.z, + })/* /Mat4::identity() */; + + let light_all_mat = l_r * directed_proj_mat * light_view_mat; + // let bounds1 = fit_psr(light_all_mat/* * inverse_visible*/, light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + let bounds0 = fit_psr(/*l_r*/light_all_mat/* * inverse_visible*/, visible_light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + // let bounds1 = fit_psr(light_all_mat/* * inverse_visible*/, aabb_to_points(visible_bounds).iter().copied(), |p| Vec3::from(p) / p.w); + // let mut light_focus_pos: Vec3 = Vec3::from(light_all_mat * Vec4::from_point(focus_pos.map(f32::fract))); + let mut light_focus_pos: Vec3 = Vec3::zero();//bounds0.center();// l_r * directed_proj_mat * light_view_mat * Vec4::from_point(focus_pos.map(|e| e.fract())); + // let mut light_focus_pos: Vec3 = bounds0.center();// l_r * directed_proj_mat * light_view_mat * Vec4::from_point(focus_pos.map(|e| e.fract())); + // println!("cam_pos: {:?}, focus_pos: {:?}, light_focus_pos: {:?}, v_p: {:?} bounds: {:?}, l_r: {:?}, light_view_mat: {:?}, light_all_mat: {:?}", cam_pos, focus_pos - focus_off, light_focus_pos, v_p, /*bounds1*/bounds0, l_r, light_view_mat, light_all_mat); + // let w_v = Mat4::translation_3d(-Vec3::new(xmax + xmin, ymax + ymin, /*zmax + zmin*/0.0) / 2.0); + + // let dot_prod = /*new_dir*//*up_dir*/view_dir.map(f64::from).dot(directed_light_dir.map(f64::from)); + // let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt();//.clamped(1e-1, 1.0); + // let sin_gamma = 0.0; + // let factor = -1.0;//1.0 / sin_gamma; + // println!("Warp factor for γ (sin γ = {:?}, γ = {:?}, near_plane = {:?}, fov = {:?}, scalar fov = {:?}, aspect ratio = {:?}): η = {:?}", sin_gamma, gamma.to_degrees(), camera::NEAR_PLANE, fov.to_degrees(), scalar_fov.to_degrees(), aspect_ratio, factor); + /* v ---l + \ Θ| + \| */ + + // let directed_near = /*0.5*//*0.25*/f64::from(camera::NEAR_PLANE);/*1.0*/;//bounds0.min.y.max(1.0); + // let z_n = /*f64::from(bounds0.min.y)*//*factor * *//*f64::from(*/directed_near/*)*/;// / /*sin_gamma*/scalar_fov.cos();// / sin_gamma; //often 1 + let d = f64::from(bounds0.max.y - bounds0.min.y/*directed_near*/).abs(); //perspective transform depth //light space y extents + // let z_f = z_n + d * camera::FAR_PLANE/* / scalar_fov.cos()*/; + // let z_0 = f64::from(bounds0.min.y); + + // Vague idea: project z_n from the camera view to the light view (where it's + // tilted by γ). + let z_0 = z_n / sin_gamma;// / sin_gamma; + // let z_1 = z_0 + d; + // Vague idea: project d from the light view back to the camera view (undoing the + // tilt by γ). + let z_1 = /*z_n*/z_0 + d * sin_gamma; + let w_l_y = /* z_f - z_n */d;/*/*f64::from(camera::FAR_PLANE - camera::NEAR_PLANE)*//*(z_f - z_n)*/d * scalar_fov.cos();*/ + // let z_f = z_n + d; + // let near_dist = directed_near; + // let factor = -1.0; + /* let factor = if factor == -1.0 { + -1.0 + } else { + 0.0 + }; */ + + // NOTE: See section 5.1.2.2 of Lloyd's thesis. + let alpha = z_1 / z_0/*z_f / z_n*/; + let alpha_sqrt = alpha.sqrt(); + let directed_near_normal = if factor < 0.0 { + // Standard shadow map to LiSPSM + (1.0 + alpha_sqrt - factor * (alpha - 1.0)) / ((alpha - 1.0) * (factor + 1.0)) + // 1+sqrt(z_f/z_n)/((z_f/z_n - 1)*2) + } else { + // LiSPSM to PSM + ((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip() + // LiSPSM: 1 / ((√α - 1) * (η√α + 1)) + // = 1 / ((√α - 1)(1)) + // = 1 / (√α - 1) + // = (1 + √α) / (α - 1) + // = (a + √(z_f/z_n)) / (z_f/z_n - 1) + }; + // let factor = -1.0; + + // Equation 5.14 - 5.16 + // let directed_near_normal = 1.0 / d * (z_0 + (z_0 * z_1).sqrt()); + // let directed_near = w_l_y / d * (z_0 + (z_0 * z_1).sqrt()); + /* let directed_near = directed_near_normal as f32; + let directed_far = (directed_near_normal + d) as f32; */ + let directed_near = (w_l_y * directed_near_normal).abs() as f32; + let directed_far = (w_l_y * (directed_near_normal + 1.0)).abs() as f32; + let (directed_near, directed_far) = (directed_near.min(directed_far), directed_near.max(directed_far)); + // let directed_near = w_l_y / d * (z_0 + (z_0 * z_1).sqrt()); + // println!("θ = {:?} η = {:?} z_n = {:?} z_f = {:?} γ = {:?} d = {:?} z_0 = {:?} z_1 = {:?} w_l_y: {:?} α = {:?} √α = {:?} n'₀ = {:?} n' = {:?} f' = {:?}", scalar_fov.to_degrees(), factor, z_n, z_f, gamma.to_degrees(), d, z_0, z_1, w_l_y, alpha, alpha_sqrt, directed_near_normal, directed_near, directed_far); + + // let directed_near = /*camera::NEAR_PLANE / sin_gamma*/camera::NEAR_PLANE; + //let near_dist = directed_near as f32; + // let directed_far = directed_near + (camera::FAR_PLANE - camera::NEAR_PLANE); + /* // let directed_near = 1.0; + let directed_near = ((z_n + (z_f * z_n).sqrt()) / /*sin_gamma*/factor) as f32; //1.0; */ + // let directed_far = directed_near + d as f32; + // println!("view_dir: {:?}, new_dir: {:?}, directed_light_dir: {:?}, dot_prod: {:?}, sin_gamma: {:?}, near_dist: {:?}, d: {:?}, z_n: {:?}, z_f: {:?}, directed_near: {:?}, directed_far: {:?}", view_dir, new_dir, directed_light_dir, dot_prod, sin_gamma, near_dist, d, z_n, z_f, directed_near, directed_far); + /* let size1 = bounds1.half_size(); + let center1 = bounds1.center(); */ + /* let look_at = cam_pos - (directed_near - near_dist) * up; + let light_all_mat: Mat4 = Mat4::look_at_rh(look_at, look_at + directed_light_dir, /*Vec3::up()*//*Vec3::from(view_dir)*//*up*//*Vec3::down()*/up); */ + // let look_at = look_at - (directed_near - near_dist) * up; + // let light_view_mat = l_r * Mat4::look_at_rh(look_at - sun_dir * light_scale, look_at, /*Vec3::up()*//*Vec3::from(view_dir)*/up); + // let w_v: Mat4 = Mat4::identity(); + // let w_v: Mat4 = Mat4::translation_3d(/*-bounds1.center()*/-center1); + //new observer point n-1 behind eye position + //pos = eyePos-up*(n-nearDist) + // let directed_near = if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 { directed_near } else { near_dist/*0.0*//*-(near_dist *//*- light_focus_pos.y)*/ }; + light_focus_pos.y = if factor != -1.0 { + /*near_dist*/z_n as f32 - directed_near + } else { + light_focus_pos.y + }; + let w_v: Mat4 = Mat4::translation_3d(/*-bounds1.center()*/-Vec3::new(light_focus_pos.x, light_focus_pos.y/* + (directed_near - near_dist)*/,/* - /*(directed_near - near_dist)*/directed_near*//*bounds1.center().z*//*directed_near*//*bounds1.min.z - *//*(directed_near - near_dist)*//*focus_pos.z*//*light_focus_pos.z*//*light_focus_pos.z*//*center1.z*//*center1.z.max(0.0)*/light_focus_pos.z)); + // let w_v: Mat4 = Mat4::translation_3d(/*-bounds1.center()*/-Vec3::new(light_focus_pos.x, light_focus_pos.y,/* - /*(directed_near - near_dist)*/directed_near*//*bounds1.center().z*//*directed_near*//*bounds1.min.z - *//*(directed_near - near_dist)*//*focus_pos.z*//*light_focus_pos.z*//*light_focus_pos.z*/center1.z + directed_near - near_dist)); + // let w_v: Mat4 = Mat4::translation_3d(/*-bounds1.center()*/-Vec3::new(0.0, 0.0,/* - /*(directed_near - near_dist)*/directed_near*//*bounds1.center().z*//*directed_near*//*bounds1.min.z - *//*(directed_near - near_dist)*//*focus_pos.z*//*light_focus_pos.z*/directed_near - near_dist)); + /* let w_p: Mat4 = Mat4::orthographic_rh_no/*frustum_rh_no*/(FrustumPlanes { + // TODO: Consider adjusting resolution based on view distance. + left: -1.0// + (center1.x - focus_pos.x) / size1.w, + // left: -(directed_shadow_resx as f32) / 2.0, + right: 1.0// + (center1.x - focus_pos.x) / size1.w, + // right: directed_shadow_res.x as f32 / 2.0, + bottom: -1.0// + (center1.y - focus_pos.y) / size1.h, + // bottom: -(directed_shadow_res.y as f32) / 2.0, + top: 1.0// + (center1.y - focus_pos.y) / size1.h, + // top: directed_shadow_res.y as f32 / 2.0, + // TODO: Swap fixed near and far planes for something dependent on the height of the + // current scene. + near: directed_near, + far: directed_far,// directed_near + /*zmax - zmin*/bounds1.max.z - bounds1.min.z,//directed_far, + }); */ + let shadow_view_mat: Mat4 = w_v * light_all_mat; + let _bounds0 = fit_psr(/*l_r*/shadow_view_mat/* * inverse_visible*/, visible_light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + // let factor = -1.0; + let w_p: Mat4 = { + if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 { + // Projection for y + let n = directed_near;// - near_dist; + let f = directed_far; + let l = -1.0;// bounds0.min.x;//-1.0;// bounds0.min.x - light_focus_pos.x; + let r = 1.0;// bounds0.max.x;//1.0;// bounds0.max.x - light_focus_pos.x; + let b = -1.0;// bounds0.max.z;// bounds0.max.z - light_focus_pos.z; + let t = 1.0;// bounds0.min.z;// bounds0.min.z - light_focus_pos.z; + let s_x = 2.0 * n / (r - l); + let o_x = (r + l) / (r - l); + let s_z = 2.0 * n / (t - b); + let o_z = (t + b) / (t - b); + + let s_y = (f + n) / (f - n); + let o_y = -2.0 * f * n / (f - n); + // y(y₀) = s_y y₀ + o_y + // = ((f + n)y₀ - 2fn) / (f - n) + // y(f) = s_y f + o_y + // = ((f + n)f - 2fn) / (f - n) + // = (f² + fn - 2fn) / (f - n) + // = (f² - fn) / (f - n) + // = f(f - n) / (f - n) + // = f + // + // y(n) = s_y n + o_y + // = ((f + n)n - 2fn) / (f - n) + // = (fn + n² - 2fn) / (f - n) + // = (n² - fn) / (f - n) + // = n(n - f) / (f - n) + // = -n + // + // x(y₀) = s_x x₀ + o_x y₀ + // = (2n x₀ + (r + l) y₀) / (r - l) + // = (2n x₀ + 2ly₀ + (r - l) y₀) / (r - l) + // = 2(n x₀ + l y₀) / (r - l) + y₀ + // = (2(n l + l n) + 2(n (x₀ - n) + l (y₀ - l))) / (r - l) + y₀ + // = (2(n l + l n) + 2(n (x₀ - n) + l (y₀ - l))) / (r - l) + y₀ + // + // = 2n(x₀ - l) / (r - l) + 2n l / (r - l) + (r + l) / (r - l)y₀ + // + // = 2 + // + // = (2 (x₀ n + l x₀) / (r - l) + y₀ + // + // = (2n x₀ - (r + l) y₀) / (r - l) + // = (2 (x₀ n - l y₀) - (r - l) y₀) / (r - l) + // = 2 (x₀ n - l y₀) / (r - l) - y₀ + // + // ~ 2(x₀ n / y₀ - l) / (r - l) - 1 + // + // = 2 (x₀ (y₀ + n - y₀) - l y₀) / (r - l) - y₀ + // = 2 (x₀ - l) y₀ / (r - l) - x₀(y₀ - n) / (r - l) - y₀ + // + // x(n) = 2 (x₀ n - l n) / (r - l) - n + // = n (2(x₀ - l) / (r - l) - 1) + // + // x(f) = 2 (x₀ n - l f) / (r - l) - f + // = f (2(x₀ (n / f) - l) / (r - l) - 1) + // + // x(f) = 2 (x₀ f + l y₀) / (r - l) - f + Mat4::new( + s_x, o_x, 0.0, 0.0, + 0.0, s_y, 0.0, o_y, + 0.0, o_z, s_z, 0.0, + 0.0, 1.0, 0.0, 0.0, + )/* + Mat4::new( + n/*1.0*/, 0.0, 0.0, 0.0, + 0.0, s_y, 0.0, o_y, + 0.0, 0.0, n, 0.0, + 0.0, 1.0, 0.0, 0.0, + )*/ + } else { + /* Mat4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, s_y, o_y, + 0.0, 0.0, 1.0, 0.0, + ) */ + Mat4::identity() + } + // Mat4::identity() + /* let a = (n + f) / (n - f); + let b = 2.0 * n * f / (n - f); + Mat4::new( + n, 0.0, 0.0, 0.0, + 0.0, n, 0.0, 0.0, + 0.0, 0.0, a, b, + 0.0, 0.0, -1.0, 0.0, + ) */ + }; + /* let a = (directed_far + directed_near) / (directed_far - directed_near); + let b = -2.0 * directed_far * directed_near / (directed_far - directed_near); + let w_p: Mat4 = Mat4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, a, 0.0, b, + 0.0, 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + ); */ + let _w_p_arr = w_p.cols.iter().map(|e| (e.x, e.y, e.z, e.w)).collect::>(); + // println!("mat4 w_p = mat4(vec4{:?}, vec4{:?}, vec4{:?}, vec4{:?});", w_p_arr[0], w_p_arr[1], w_p_arr[2], w_p_arr[3]); + // let w_p: Mat4 = Mat4::identity(); + // let zmin = p1.z.min(p4.z); + // let zmax = p1.z.max(p4.z); + // println!("zmin: {:?}, zmax: {:?}", zmin, zmax); + + // let directed_near = 1.0; + // let directed_far = /*loaded_distance * 2.0*/(zmax - zmin) * 2.0 + directed_near; + + /* let directed_proj_mat = Mat4::orthographic_rh_no(FrustumPlanes { + // TODO: Consider adjusting resolution based on view distance. + left: xmin, + // left: -(directed_shadow_res.x as f32) / 2.0, + right: xmax, + // right: directed_shadow_res.x as f32 / 2.0, + bottom: ymin, + // bottom: -(directed_shadow_res.y as f32) / 2.0, + top: ymax, + // top: directed_shadow_res.y as f32 / 2.0, + // TODO: Swap fixed near and far planes for something dependent on the height of the + // current scene. + near: zmin,//directed_near, + far: zmax,//directed_far, + }); */ + let shadow_all_mat: Mat4 = w_p * shadow_view_mat/*w_v * light_all_mat*/; + let _w_p_arr = shadow_all_mat.cols.iter().map(|e| (e.x, e.y, e.z, e.w)).collect::>(); + // println!("mat4 shadow_all_mat = mat4(vec4{:?}, vec4{:?}, vec4{:?}, vec4{:?});", w_p_arr[0], w_p_arr[1], w_p_arr[2], w_p_arr[3]); + let Aabb { min: Vec3 { x: xmin, y: ymin, z: zmin }, max: Vec3 { x: xmax, y: ymax, z: zmax } } = + fit_psr(/*light_all_mat*/shadow_all_mat/*shadow_view_mat*//* * inverse_visible*/, visible_light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + // fit_psr(light_all_mat/* * inverse_visible*/, aabb_to_points(visible_bounds).iter().copied(), |p| Vec3::from(p) / p.w); + /* let Aabb { min: Vec3 { z: zmin, .. }, max: Vec3 { z: zmax, .. } } = + fit_psr(/*light_all_mat*/shadow_all_mat/* * inverse_visible*/, light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + // fit_psr(light_all_mat/* * inverse_visible*/, light_volume.iter().copied(), |p| Vec3::from(p) / p.w); + // fit_psr(light_all_mat/* * inverse_visible*/, aabb_to_points(visible_bounds).iter().copied(), |p| Vec3::from(p) / p.w); */ + // println!("xmin: {:?} ymin: {:?} zmin: {:?}, xmax: {:?}, ymax: {:?}, zmax: {:?}", xmin, ymin, zmin, xmax, ymax, zmax); + let s_x = 2.0 / (xmax - xmin); + let s_y = 2.0 / (ymax - ymin); + let s_z = 2.0 / (zmax - zmin); + /* let o_x = -(s_x * (xmax + xmin)) / 2.0; + let o_y = -(s_y * (ymax + ymin)) / 2.0; + let o_z = -(s_z * (zmax + zmin)) / 2.0; */ + let o_x = -(xmax + xmin) / (xmax - xmin); + let o_y = -(ymax + ymin) / (ymax - ymin); + let o_z = -(zmax + zmin) / (zmax - zmin); + let directed_proj_mat = if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 { + // Mat4::identity() + Mat4::new( + s_x, 0.0, 0.0, o_x, + 0.0, s_y, 0.0, o_y, + 0.0, 0.0, /*-*/s_z, /*-*/o_z, + 0.0, 0.0, 0.0, 1.0, + )/*.scaled_3d(Vec3::new(1.0, 1.0, -1.0))*/ + } else { + Mat4::new( + s_x, 0.0, 0.0, o_x, + 0.0, s_y, 0.0, o_y, + 0.0, 0.0, s_z, o_z, + 0.0, 0.0, 0.0, 1.0, + )/*.scaled_3d(Vec3::new(1.0, 1.0, -1.0))*/ + }/*.scaled_3d(Vec3::new(1.0, 1.0, -1.0))*//* * w_p * w_v*//* * l_r*/;//Mat4::identity(); + // println!("proj_mat: {:?}", directed_proj_mat); + // println!("all_mat: {:?}", directed_proj_mat * view_mat); + let _w_p_arr = directed_proj_mat.cols.iter().map(|e| (e.x, e.y, e.z, e.w)).collect::>(); + // println!("mat4 directed_proj_mat = mat4(vec4{:?}, vec4{:?}, vec4{:?}, vec4{:?});", w_p_arr[0], w_p_arr[1], w_p_arr[2], w_p_arr[3]); + + let _w_p_arr = (directed_proj_mat * shadow_all_mat).cols.iter().map(|e| (e.x, e.y, e.z, e.w)).collect::>(); + // println!("mat4 final_mat = mat4(vec4{:?}, vec4{:?}, vec4{:?}, vec4{:?});", w_p_arr[0], w_p_arr[1], w_p_arr[2], w_p_arr[3]); + + let directed_texture_proj_mat = texture_mat * directed_proj_mat; + ShadowLocals::new(directed_proj_mat * shadow_all_mat, directed_texture_proj_mat * shadow_all_mat) + })); + // Now, we tackle point lights. + // First, create a perspective projection matrix at 90 degrees (to cover a whole + // face of the cube map we're using). + let shadow_proj = Mat4::perspective_rh_no( + 90.0f32.to_radians(), + point_shadow_aspect, + SHADOW_NEAR, + SHADOW_FAR, + ); + // Next, construct the 6 orientations we'll use for the six faces, in terms of + // their (forward, up) vectors. + let orientations = [ + (Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, -1.0, 0.0)), + (Vec3::new(-1.0, 0.0, 0.0), Vec3::new(0.0, -1.0, 0.0)), + (Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 0.0, 1.0)), + (Vec3::new(0.0, -1.0, 0.0), Vec3::new(0.0, 0.0, -1.0)), + (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, -1.0, 0.0)), + (Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, -1.0, 0.0)), + ]; + // NOTE: We could create the shadow map collection at the same time as the + // lights, but then we'd have to sort them both, which wastes time. Plus, we + // want to prepend our directed lights. + shadow_mats.extend(lights.iter().flat_map(|light| { + // Now, construct the full projection matrix by making the light look at each + // cube face. + let eye = Vec3::new(light.pos[0], light.pos[1], light.pos[2]) - focus_off; + orientations.iter().map(move |&(forward, up)| { + // NOTE: We don't currently try to linearize point lights or need a separate + // transform for them. + ShadowLocals::new( + shadow_proj * Mat4::look_at_rh(eye, eye + forward, up), + Mat4::identity(), + ) + }) + })); + + /* shadow_mats.push( + Mat4::orthographic_rh_no + float near_plane = 1.0f, far_plane = 7.5f; + glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); + + ); */ + renderer + .update_consts(&mut self.shadow_mats, &shadow_mats) + .expect("Failed to update light constants"); + // renderer + // .update_shadow_consts(&mut self.shadow_mats, &shadow_mats, 0, + // 6) .expect("Failed to update light constants"); + } // Remove unused figures. self.figure_mgr.clean(scene_data.tick); @@ -490,39 +1877,45 @@ impl Scene { tick: u64, scene_data: &SceneData, ) { - // Render terrain and figures. - self.terrain.render( - renderer, - &self.globals, - &self.lights, - &self.shadows, - &self.shadow_mats, - self.lod.get_data(), - self.camera.get_focus_pos(), - ); - self.figure_mgr.render( - renderer, - state, - player_entity, - tick, - &self.globals, - &self.lights, - &self.shadows, - self.lod.get_data(), - &self.camera, - scene_data.figure_lod_render_distance, - ); - self.lod.render(renderer, &self.globals); + let sun_dir = scene_data.get_sun_dir(); + let is_daylight = sun_dir.z < 0.0/*0.6*/; + let focus_pos = self.camera.get_focus_pos(); + let cam_pos = self.camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); - // Render the skybox. + // would instead have this as an extension. + if renderer.render_mode().shadow == render::ShadowMode::Map + && (is_daylight || self.light_data.len() > 0) + { + // Set up shadow mapping. + renderer.start_shadows(); + + // Render terrain shadows. + self.terrain.render_shadows( + renderer, + &self.globals, + &self.shadow_mats, + &self.light_data, + is_daylight, + focus_pos, + ); + + // Render figure shadows. + self.figure_mgr.render_shadows( + renderer, + state, + tick, + &self.globals, + &self.shadow_mats, + is_daylight, + &self.light_data, + &self.camera, + scene_data.figure_lod_render_distance, + ); + + // Flush shadows. + renderer.flush_shadows(); + } let lod = self.lod.get_data(); - renderer.render_skybox( - &self.skybox.model, - &self.globals, - &self.skybox.locals, - &lod.map, - &lod.horizon, - ); self.figure_mgr.render_player( renderer, @@ -532,18 +1925,56 @@ impl Scene { &self.globals, &self.lights, &self.shadows, + &self.shadow_mats, lod, &self.camera, scene_data.figure_lod_render_distance, ); + // Render terrain and figures. + self.terrain.render( + renderer, + &self.globals, + &self.lights, + &self.shadows, + &self.shadow_mats, + lod, + focus_pos, + ); + + self.figure_mgr.render( + renderer, + state, + player_entity, + tick, + &self.globals, + &self.lights, + &self.shadows, + &self.shadow_mats, + lod, + &self.camera, + scene_data.figure_lod_render_distance, + ); + self.lod.render(renderer, &self.globals); + + // Render the skybox. + renderer.render_skybox( + &self.skybox.model, + &self.globals, + &self.skybox.locals, + &lod.map, + &lod.horizon, + ); + self.terrain.render_translucent( renderer, &self.globals, &self.lights, &self.shadows, + &self.shadow_mats, lod, - self.camera.get_focus_pos(), + focus_pos, + cam_pos, scene_data.sprite_render_distance, ); diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 5e62e6f2f7..7fa4916700 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -4,14 +4,15 @@ use crate::{ fixture::FixtureSkeleton, Animation, Skeleton, }, - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Mesh, Model, - PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, + create_pp_mesh, create_skybox_mesh, BoneMeshes, Consts, FigureModel, FigurePipeline, + Globals, Light, Model, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, + ShadowLocals, SkyboxLocals, SkyboxPipeline, }, scene::{ camera::{self, Camera, CameraMode}, - figure::{load_mesh, FigureModelCache, FigureState}, + figure::{load_mesh, FigureColLights, FigureModelCache, FigureState}, LodData, }, window::{Event, PressState}, @@ -43,8 +44,17 @@ impl ReadVol for VoidVol { fn get<'a>(&'a self, _pos: Vec3) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) } } -fn generate_mesh(segment: &Segment, offset: Vec3) -> Mesh { - Meshable::::generate_mesh(segment, (offset, Vec3::one())).0 +fn generate_mesh<'a>( + greedy: &mut GreedyMesh<'a>, + segment: Segment, + offset: Vec3, +) -> BoneMeshes { + let (opaque, _, /* shadow */ _, bounds) = + Meshable::::generate_mesh( + segment, + (greedy, offset, Vec3::one()), + ); + (opaque /* , shadow */, bounds) } struct Skybox { @@ -61,14 +71,16 @@ pub struct Scene { globals: Consts, lights: Consts, shadows: Consts, + shadow_mats: Consts, camera: Camera, skybox: Skybox, postprocess: PostProcess, lod: LodData, map_bounds: Vec2, - backdrop: Option<(Model, FigureState)>, + col_lights: FigureColLights, + backdrop: Option<(FigureModel, FigureState)>, figure_model_cache: FigureModelCache, figure_state: FigureState, @@ -109,11 +121,13 @@ impl Scene { camera.set_distance(3.4); camera.set_orientation(Vec3::new(start_angle, 0.0, 0.0)); + let mut col_lights = FigureColLights::new(renderer); + Self { globals: renderer.create_consts(&[Globals::default()]).unwrap(), lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), - camera, + shadow_mats: renderer.create_consts(&[ShadowLocals::default(); 6]).unwrap(), skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), @@ -133,6 +147,13 @@ impl Scene { backdrop: backdrop.map(|specifier| { let mut state = FigureState::new(renderer, FixtureSkeleton::new()); + let mut greedy = FigureModel::make_greedy(); + let mesh = load_mesh( + specifier, + Vec3::new(-55.0, -49.5, -2.0), + |segment, offset| generate_mesh(&mut greedy, segment, offset), + ); + let model = col_lights.create_figure(renderer, greedy, mesh).unwrap(); state.update( renderer, Vec3::zero(), @@ -141,21 +162,20 @@ impl Scene { Rgba::broadcast(1.0), 15.0, // Want to get there immediately. 1.0, + &model, 0, true, false, + &camera, ); ( - renderer - .create_model(&load_mesh( - specifier, - Vec3::new(-55.0, -49.5, -2.0), - generate_mesh, - )) - .unwrap(), - state + model, + state, ) }), + col_lights, + + camera, turning: false, char_ori: /*0.0*/-start_angle, @@ -190,7 +210,12 @@ impl Scene { } } - pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: SceneData) { + pub fn maintain( + &mut self, + renderer: &mut Renderer, + scene_data: SceneData, + loadout: Option<&Loadout>, + ) { self.camera.update( scene_data.time, /* 1.0 / 60.0 */ scene_data.delta_time, @@ -233,7 +258,8 @@ impl Scene { error!("Renderer failed to update: {:?}", err); } - self.figure_model_cache.clean(scene_data.tick); + self.figure_model_cache + .clean(&mut self.col_lights, scene_data.tick); if let Some(body) = scene_data.body { let tgt_skeleton = IdleAnimation::update_skeleton( @@ -246,20 +272,34 @@ impl Scene { self.figure_state .skeleton_mut() .interpolate(&tgt_skeleton, scene_data.delta_time); - } - self.figure_state.update( - renderer, - Vec3::zero(), - Vec3::new(self.char_ori.sin(), -self.char_ori.cos(), 0.0), - 1.0, - Rgba::broadcast(1.0), - scene_data.delta_time, - 1.0, - 0, - true, - false, - ); + let model = &self + .figure_model_cache + .get_or_create_model( + renderer, + &mut self.col_lights, + Body::Humanoid(body), + loadout, + scene_data.tick, + CameraMode::default(), + None, + ) + .0; + self.figure_state.update( + renderer, + Vec3::zero(), + Vec3::new(self.char_ori.sin(), -self.char_ori.cos(), 0.0), + 1.0, + Rgba::broadcast(1.0), + scene_data.delta_time, + 1.0, + &model[0], + 0, + true, + false, + &self.camera, + ); + } } pub fn render( @@ -282,6 +322,7 @@ impl Scene { .figure_model_cache .get_or_create_model( renderer, + &mut self.col_lights, Body::Humanoid(body), loadout, tick, @@ -292,11 +333,13 @@ impl Scene { renderer.render_figure( &model[0], + &self.col_lights.texture(), &self.globals, self.figure_state.locals(), self.figure_state.bone_consts(), &self.lights, &self.shadows, + &self.shadow_mats, &self.lod.map, &self.lod.horizon, ); @@ -305,11 +348,13 @@ impl Scene { if let Some((model, state)) = &self.backdrop { renderer.render_figure( model, + &self.col_lights.texture(), &self.globals, state.locals(), state.bone_consts(), &self.lights, &self.shadows, + &self.shadow_mats, &self.lod.map, &self.lod.horizon, ); diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 4bec55d70d..45955eba40 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1,9 +1,9 @@ use crate::{ - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, Shadow, - ShadowLocals, ShadowPipeline, SpriteInstance, SpritePipeline, TerrainLocals, - TerrainPipeline, Texture, + self, ColLightFmt, ColLightInfo, Consts, FluidPipeline, Globals, Instances, Light, Mesh, + Model, RenderError, Renderer, Shadow, ShadowLocals, ShadowPipeline, SpriteInstance, + SpriteLocals, SpritePipeline, TerrainLocals, TerrainPipeline, Texture, }, }; @@ -16,24 +16,41 @@ use common::{ vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox}, volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError}, }; +use core::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration}; use crossbeam::channel; use dot_vox::DotVoxData; +use guillotiere::AtlasAllocator; use hashbrown::HashMap; -use std::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration}; +use std::sync::Arc; use treeculler::{BVol, Frustum, AABB}; use vek::*; +const SPRITE_SCALE: Vec3 = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0); + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum Visibility { + OutOfRange = 0, + InRange = 1, + Visible = 2, +} + struct TerrainChunkData { // GPU data load_time: f32, opaque_model: Model, fluid_model: Option>, - shadow_model: Model, - sprite_instances: HashMap<(BlockKind, usize), Instances>, + // shadow_model: Model, + // col_lights: Texture, + col_lights: guillotiere::AllocId, + sprite_instances: HashMap< + (BlockKind, usize), + Instances, /* RawBuffer*//*(Consts, usize) */ + >, locals: Consts, - visible: bool, - can_shadow: bool, + visible: Visibility, + can_shadow_point: bool, + can_shadow_sun: bool, z_bounds: (f32, f32), frustum_last_plane_index: u8, } @@ -51,7 +68,7 @@ struct MeshWorkerResponse { z_bounds: (f32, f32), opaque_mesh: Mesh, fluid_mesh: Mesh, - shadow_mesh: Mesh, + col_lights_info: ColLightInfo, sprite_instances: HashMap<(BlockKind, usize), Vec>, started_tick: u64, } @@ -256,15 +273,20 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( z_bounds: (f32, f32), started_tick: u64, volume: as SampleVol>>::Sample, + max_texture_size: u16, range: Aabb, + sprite_models: &HashMap<(BlockKind, usize), Vec */ SpriteData>>, ) -> MeshWorkerResponse { - let (opaque_mesh, fluid_mesh, shadow_mesh) = volume.generate_mesh(range); + let (opaque_mesh /* col_lights */, fluid_mesh, _shadow_mesh, (bounds, col_lights_info)) = + volume.generate_mesh((range, Vec2::new(max_texture_size, max_texture_size))); + // println!("z_bounds{:?}, bounds: {:?}", z_bounds, (bounds.min.z, + // bounds.max.z)); MeshWorkerResponse { pos, - z_bounds, + z_bounds: (bounds.min.z, bounds.max.z), opaque_mesh, fluid_mesh, - shadow_mesh, + col_lights_info, // Extract sprite locations from volume sprite_instances: { let mut instances = HashMap::new(); @@ -272,8 +294,8 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( for x in 0..V::RECT_SIZE.x as i32 { for y in 0..V::RECT_SIZE.y as i32 { for z in z_bounds.0 as i32..z_bounds.1 as i32 + 1 { - let wpos = Vec3::from(pos * V::RECT_SIZE.map(|e: u32| e as i32)) - + Vec3::new(x, y, z); + let rel_pos = Vec3::new(x, y, z); + let wpos = Vec3::from(pos * V::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos; let block = volume.get(wpos).ok().copied().unwrap_or(Block::empty()); @@ -281,20 +303,38 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( let seed = wpos.x as u64 * 3 + wpos.y as u64 * 7 + wpos.x as u64 * wpos.y as u64; // Awful PRNG - let ori = block.get_ori().unwrap_or((seed % 4) as u8 * 2); - + let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111; + let variation = seed as usize % cfg.variations; + let key = (block.kind(), variation); + // NOTE: Safe bbecause we called sprite_config_for already. + // NOTE: Safe because 0 ≤ ori < 8 + let sprite_data = &sprite_models[&key][0]; let instance = SpriteInstance::new( Mat4::identity() + /*sprite_models[&key][0].mat + /* .scaled_3d( + lod_scale + ) */ + /* .translated_3d( + offset + ) */ + // * 1.0 / 11.0 + .rotated_z(f32::consts::PI * 0.25 * ori as f32) + */ + .translated_3d(sprite_data.offset) + // .scaled_3d(SPRITE_SCALE) .rotated_z(f32::consts::PI * 0.25 * ori as f32) .translated_3d( - wpos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0), + (rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)) / SPRITE_SCALE, ), - Rgb::broadcast(1.0), + // Rgb::broadcast(1.0), cfg.wind_sway, + rel_pos, + ori, ); instances - .entry((block.kind(), seed as usize % cfg.variations)) + .entry(key) .or_insert_with(|| Vec::new()) .push(instance); } @@ -308,9 +348,23 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( } } -pub struct Terrain { - chunks: HashMap, TerrainChunkData>, +struct SpriteData { + /* mat: Mat4, */ + locals: Consts, + model: Model, + /* scale: Vec3, */ + offset: Vec3, +} +pub struct Terrain { + atlas: AtlasAllocator, + chunks: HashMap, TerrainChunkData>, + /* /// Secondary index into the terrain chunk table, used to sort through chunks by z index from + /// the top down. + z_index_down: BTreeSet>, + /// Secondary index into the terrain chunk table, used to sort through chunks by z index from + /// the bottom up. + z_index_up: BTreeSet>, */ // The mpsc sender and receiver used for talking to meshing worker threads. // We keep the sender component for no reason other than to clone it and send it to new // workers. @@ -319,1558 +373,1277 @@ pub struct Terrain { mesh_todo: HashMap, ChunkMeshState>, // GPU data - sprite_models: HashMap<(BlockKind, usize), Vec>>, + // sprite_model_data: Model, + sprite_models: Arc */ SpriteData>>>, + sprite_col_lights: Texture, + col_lights: Texture, waves: Texture, phantom: PhantomData, } +impl TerrainChunkData { + pub fn can_shadow_sun(&self) -> bool { + self.visible == Visibility::Visible || self.can_shadow_sun + } +} + impl Terrain { pub fn new(renderer: &mut Renderer) -> Self { // Create a new mpsc (Multiple Produced, Single Consumer) pair for communicating // with worker threads that are meshing chunks. let (send, recv) = channel::unbounded(); - let mut make_models = |s, offset, lod_axes: Vec3| { + let (atlas, col_lights) = + Self::make_atlas(renderer).expect("Failed to create atlas texture"); + + let max_texture_size = renderer.max_texture_size(); + let max_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + // let max_sprite_size = guillotiere::Size::new(128, 64); + let mut greedy = GreedyMesh::new(max_size); + // let mut mesh = Mesh::new(); + let mut locals_buffer = [SpriteLocals::default(); 8]; + let mut make_models = |(kind, variation), s, offset, lod_axes: Vec3| { let scaled = [1.0, 0.8, 0.6, 0.4, 0.2]; - scaled - .iter() - .map(|lod_scale| { - if *lod_scale == 1.0 { - Vec3::broadcast(1.0) - } else { - lod_axes * *lod_scale + lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 }) - } - }) - .map(|lod_scale| { - renderer - .create_model( - &Meshable::::generate_mesh( - &Segment::from(assets::load_expect::(s).as_ref()) - .scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0, - ) - .unwrap() - }) - .collect::>() + let model = assets::load_expect::(s); + let model_size = model + .models + .first() + .map( + |&dot_vox::Model { + size: dot_vox::Size { x, y, z }, + .. + }| Vec3::new(x, y, z), + ) + .unwrap_or(Vec3::zero()); + let max_model_size = Vec3::new(15.0, 15.0, 63.0); + let model_scale = max_model_size.map2(model_size, |max_sz: f32, cur_sz| { + let scale = max_sz / max_sz.max(cur_sz as f32); + if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz { + scale - 0.001 + } else { + scale + } + }); + /* println!( + "model_size: {:?} (model_scale = {:?})", + model_size, model_scale + ); */ + let wind_sway = sprite_config_for(kind).map(|c| c.wind_sway).unwrap_or(0.0); + let sprite_mat: Mat4 = Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE); + ( + (kind, variation), + scaled + .iter() + .map(|&lod_scale_orig| { + let lod_scale = model_scale * if lod_scale_orig == 1.0 { + Vec3::broadcast(1.0) + } else { + lod_axes * lod_scale_orig + lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 }) + }; + let opaque_model = + Meshable::::generate_mesh( + Segment::from(model.as_ref()).scaled_by(lod_scale), + (&mut greedy, wind_sway >= 0.4 && lod_scale_orig == 1.0/*>= 0.8*//*lod_axes.x == 0.0 && lod_axes.y == 0.0*//* && lod_scale.z >= 0.8*//*, offset * lod_scale, Vec3::one() / lod_scale*/), + ) + .0; + let sprite_scale = Vec3::one() / lod_scale; + let sprite_mat: Mat4 = sprite_mat * Mat4::scaling_3d(sprite_scale); + locals_buffer.iter_mut().enumerate().for_each(|(ori, locals)| { + let sprite_mat = sprite_mat.rotated_z(f32::consts::PI * 0.25 * ori as f32); + *locals = SpriteLocals::new(sprite_mat, sprite_scale, offset, wind_sway); + }); + + SpriteData { + /* scale: sprite_scale, */ + offset, + /* mat: sprite_mat, */ + model: renderer.create_model(&opaque_model) + .unwrap(), + locals: renderer.create_consts(&locals_buffer) + .expect("Failed to upload sprite locals to the GPU!"), + } + /* // NOTE: Safe because atlas size is an upper bound on vertex count, and atlas + // width and height are at most u16::MAX. + let start = mesh.vertices().len() as u32; + let vbuf = (start..start + opaque_model.vertices().len() as u32); + mesh.push_mesh(&opaque_model); + vbuf */ + }) + .collect::>(), + ) }; + let sprite_models: HashMap<(BlockKind, usize), /* Vec> */ _> = vec![ + // Windows + make_models( + (BlockKind::Window1, 0), + "voxygen.voxel.sprite.window.window-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Window2, 0), + "voxygen.voxel.sprite.window.window-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Window3, 0), + "voxygen.voxel.sprite.window.window-2", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Window4, 0), + "voxygen.voxel.sprite.window.window-3", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Cacti + make_models( + (BlockKind::LargeCactus, 0), + "voxygen.voxel.sprite.cacti.large_cactus", + Vec3::new(-13.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LargeCactus, 1), + "voxygen.voxel.sprite.cacti.tall", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BarrelCactus, 0), + "voxygen.voxel.sprite.cacti.barrel_cactus", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RoundCactus, 0), + "voxygen.voxel.sprite.cacti.cactus_round", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortCactus, 0), + "voxygen.voxel.sprite.cacti.cactus_short", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MedFlatCactus, 0), + "voxygen.voxel.sprite.cacti.flat_cactus_med", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortFlatCactus, 0), + "voxygen.voxel.sprite.cacti.flat_cactus_short", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Fruit + make_models( + (BlockKind::Apple, 0), + "voxygen.voxel.sprite.fruit.apple", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Flowers + make_models( + (BlockKind::BlueFlower, 0), + "voxygen.voxel.sprite.flowers.flower_blue_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 1), + "voxygen.voxel.sprite.flowers.flower_blue_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 2), + "voxygen.voxel.sprite.flowers.flower_blue_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 3), + "voxygen.voxel.sprite.flowers.flower_blue_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 4), + "voxygen.voxel.sprite.flowers.flower_blue_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 5), + "voxygen.voxel.sprite.flowers.flower_blue_6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 6), + "voxygen.voxel.sprite.flowers.flower_blue_7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 0), + "voxygen.voxel.sprite.flowers.flower_pink_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 1), + "voxygen.voxel.sprite.flowers.flower_pink_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 2), + "voxygen.voxel.sprite.flowers.flower_pink_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PinkFlower, 3), + "voxygen.voxel.sprite.flowers.flower_pink_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 0), + "voxygen.voxel.sprite.flowers.flower_purple_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 0), + "voxygen.voxel.sprite.flowers.flower_red_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 1), + "voxygen.voxel.sprite.flowers.flower_red_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 2), + "voxygen.voxel.sprite.flowers.flower_red_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 0), + "voxygen.voxel.sprite.flowers.flower_white_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 1), + "voxygen.voxel.sprite.flowers.flower_white_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::YellowFlower, 0), + "voxygen.voxel.sprite.flowers.flower_purple_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Sunflower, 0), + "voxygen.voxel.sprite.flowers.sunflower_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Sunflower, 1), + "voxygen.voxel.sprite.flowers.sunflower_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Grass + make_models( + (BlockKind::LongGrass, 0), + "voxygen.voxel.sprite.grass.grass_long_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 1), + "voxygen.voxel.sprite.grass.grass_long_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 2), + "voxygen.voxel.sprite.grass.grass_long_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 3), + "voxygen.voxel.sprite.grass.grass_long_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 4), + "voxygen.voxel.sprite.grass.grass_long_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 5), + "voxygen.voxel.sprite.grass.grass_long_6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LongGrass, 6), + "voxygen.voxel.sprite.grass.grass_long_7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 0), + "voxygen.voxel.sprite.grass.grass_med_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 1), + "voxygen.voxel.sprite.grass.grass_med_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 2), + "voxygen.voxel.sprite.grass.grass_med_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 3), + "voxygen.voxel.sprite.grass.grass_med_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::MediumGrass, 4), + "voxygen.voxel.sprite.grass.grass_med_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 0), + "voxygen.voxel.sprite.grass.grass_short_1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 1), + "voxygen.voxel.sprite.grass.grass_short_2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 2), + "voxygen.voxel.sprite.grass.grass_short_3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 3), + "voxygen.voxel.sprite.grass.grass_short_4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShortGrass, 4), + "voxygen.voxel.sprite.grass.grass_short_5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 0), + "voxygen.voxel.sprite.mushrooms.mushroom-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 1), + "voxygen.voxel.sprite.mushrooms.mushroom-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 2), + "voxygen.voxel.sprite.mushrooms.mushroom-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 3), + "voxygen.voxel.sprite.mushrooms.mushroom-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 4), + "voxygen.voxel.sprite.mushrooms.mushroom-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 5), + "voxygen.voxel.sprite.mushrooms.mushroom-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 6), + "voxygen.voxel.sprite.mushrooms.mushroom-6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 7), + "voxygen.voxel.sprite.mushrooms.mushroom-7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 8), + "voxygen.voxel.sprite.mushrooms.mushroom-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 9), + "voxygen.voxel.sprite.mushrooms.mushroom-9", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 10), + "voxygen.voxel.sprite.mushrooms.mushroom-10", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Liana, 0), + "voxygen.voxel.sprite.lianas.liana-0", + Vec3::new(-1.5, -0.5, -88.0), + Vec3::unit_z() * 0.5, + ), + make_models( + (BlockKind::Liana, 1), + "voxygen.voxel.sprite.lianas.liana-1", + Vec3::new(-1.0, -0.5, -55.0), + Vec3::unit_z() * 0.5, + ), + make_models( + (BlockKind::Velorite, 0), + "voxygen.voxel.sprite.velorite.velorite_ore", + Vec3::new(-5.0, -5.0, -5.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 0), + "voxygen.voxel.sprite.velorite.velorite_1", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 1), + "voxygen.voxel.sprite.velorite.velorite_2", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 2), + "voxygen.voxel.sprite.velorite.velorite_3", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 3), + "voxygen.voxel.sprite.velorite.velorite_4", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 4), + "voxygen.voxel.sprite.velorite.velorite_5", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 5), + "voxygen.voxel.sprite.velorite.velorite_6", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 6), + "voxygen.voxel.sprite.velorite.velorite_7", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 7), + "voxygen.voxel.sprite.velorite.velorite_8", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 8), + "voxygen.voxel.sprite.velorite.velorite_9", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::VeloriteFrag, 9), + "voxygen.voxel.sprite.velorite.velorite_10", + Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 0), + "voxygen.voxel.sprite.chests.chest", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 1), + "voxygen.voxel.sprite.chests.chest_gold", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 2), + "voxygen.voxel.sprite.chests.chest_dark", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Chest, 3), + "voxygen.voxel.sprite.chests.chest_vines", + Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), + ), + //Welwitch + make_models( + (BlockKind::Welwitch, 0), + "voxygen.voxel.sprite.welwitch.1", + Vec3::new(-15.0, -17.0, -0.0), + Vec3::unit_z() * 0.7, + ), + //Pumpkins + make_models( + (BlockKind::Pumpkin, 0), + "voxygen.voxel.sprite.pumpkin.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 1), + "voxygen.voxel.sprite.pumpkin.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 2), + "voxygen.voxel.sprite.pumpkin.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 3), + "voxygen.voxel.sprite.pumpkin.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 4), + "voxygen.voxel.sprite.pumpkin.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 5), + "voxygen.voxel.sprite.pumpkin.6", + Vec3::new(-7.0, -6.5, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pumpkin, 6), + "voxygen.voxel.sprite.pumpkin.7", + Vec3::new(-7.0, -9.5, -0.0), + Vec3::one(), + ), + //Lingonberries + make_models( + (BlockKind::LingonBerry, 0), + "voxygen.voxel.sprite.lingonberry.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LingonBerry, 1), + "voxygen.voxel.sprite.lingonberry.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LingonBerry, 2), + "voxygen.voxel.sprite.lingonberry.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Leafy Plants + make_models( + (BlockKind::LeafyPlant, 0), + "voxygen.voxel.sprite.leafy_plant.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 1), + "voxygen.voxel.sprite.leafy_plant.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 2), + "voxygen.voxel.sprite.leafy_plant.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 3), + "voxygen.voxel.sprite.leafy_plant.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 4), + "voxygen.voxel.sprite.leafy_plant.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 5), + "voxygen.voxel.sprite.leafy_plant.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 6), + "voxygen.voxel.sprite.leafy_plant.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 7), + "voxygen.voxel.sprite.leafy_plant.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 8), + "voxygen.voxel.sprite.leafy_plant.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LeafyPlant, 9), + "voxygen.voxel.sprite.leafy_plant.10", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Ferns + make_models( + (BlockKind::Fern, 0), + "voxygen.voxel.sprite.ferns.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 1), + "voxygen.voxel.sprite.ferns.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 2), + "voxygen.voxel.sprite.ferns.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 3), + "voxygen.voxel.sprite.ferns.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 4), + "voxygen.voxel.sprite.ferns.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 5), + "voxygen.voxel.sprite.ferns.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 6), + "voxygen.voxel.sprite.ferns.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 7), + "voxygen.voxel.sprite.ferns.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 8), + "voxygen.voxel.sprite.ferns.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 9), + "voxygen.voxel.sprite.ferns.10", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 10), + "voxygen.voxel.sprite.ferns.11", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Fern, 11), + "voxygen.voxel.sprite.ferns.12", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Dead Bush + make_models( + (BlockKind::DeadBush, 0), + "voxygen.voxel.sprite.dead_bush.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DeadBush, 1), + "voxygen.voxel.sprite.dead_bush.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DeadBush, 2), + "voxygen.voxel.sprite.dead_bush.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DeadBush, 3), + "voxygen.voxel.sprite.dead_bush.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Blueberries + make_models( + (BlockKind::Blueberry, 0), + "voxygen.voxel.sprite.blueberry.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 1), + "voxygen.voxel.sprite.blueberry.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 2), + "voxygen.voxel.sprite.blueberry.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 3), + "voxygen.voxel.sprite.blueberry.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 4), + "voxygen.voxel.sprite.blueberry.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 5), + "voxygen.voxel.sprite.blueberry.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 6), + "voxygen.voxel.sprite.blueberry.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 7), + "voxygen.voxel.sprite.blueberry.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Blueberry, 8), + "voxygen.voxel.sprite.blueberry.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), + // Ember + make_models( + (BlockKind::Ember, 0), + "voxygen.voxel.sprite.ember.1", + Vec3::new(-7.0, -7.0, -2.9), + Vec3::new(1.0, 1.0, 0.0), + ), + // Corn + make_models( + (BlockKind::Corn, 0), + "voxygen.voxel.sprite.corn.corn-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 1), + "voxygen.voxel.sprite.corn.corn-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 2), + "voxygen.voxel.sprite.corn.corn-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 3), + "voxygen.voxel.sprite.corn.corn-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 4), + "voxygen.voxel.sprite.corn.corn-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Corn, 5), + "voxygen.voxel.sprite.corn.corn-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Yellow Wheat + make_models( + (BlockKind::WheatYellow, 0), + "voxygen.voxel.sprite.wheat_yellow.wheat-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 1), + "voxygen.voxel.sprite.wheat_yellow.wheat-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 2), + "voxygen.voxel.sprite.wheat_yellow.wheat-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 3), + "voxygen.voxel.sprite.wheat_yellow.wheat-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 4), + "voxygen.voxel.sprite.wheat_yellow.wheat-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 5), + "voxygen.voxel.sprite.wheat_yellow.wheat-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 6), + "voxygen.voxel.sprite.wheat_yellow.wheat-6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 7), + "voxygen.voxel.sprite.wheat_yellow.wheat-7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 8), + "voxygen.voxel.sprite.wheat_yellow.wheat-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatYellow, 9), + "voxygen.voxel.sprite.wheat_yellow.wheat-9", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Green Wheat + make_models( + (BlockKind::WheatGreen, 0), + "voxygen.voxel.sprite.wheat_green.wheat-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 1), + "voxygen.voxel.sprite.wheat_green.wheat-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 2), + "voxygen.voxel.sprite.wheat_green.wheat-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 3), + "voxygen.voxel.sprite.wheat_green.wheat-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 4), + "voxygen.voxel.sprite.wheat_green.wheat-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 5), + "voxygen.voxel.sprite.wheat_green.wheat-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 6), + "voxygen.voxel.sprite.wheat_green.wheat-6", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 7), + "voxygen.voxel.sprite.wheat_green.wheat-7", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 8), + "voxygen.voxel.sprite.wheat_green.wheat-8", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::WheatGreen, 9), + "voxygen.voxel.sprite.wheat_green.wheat-9", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Cabbage + make_models( + (BlockKind::Cabbage, 0), + "voxygen.voxel.sprite.cabbage.cabbage-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Cabbage, 1), + "voxygen.voxel.sprite.cabbage.cabbage-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Cabbage, 2), + "voxygen.voxel.sprite.cabbage.cabbage-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + // Flax + make_models( + (BlockKind::Flax, 0), + "voxygen.voxel.sprite.flax.flax-0", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 1), + "voxygen.voxel.sprite.flax.flax-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 2), + "voxygen.voxel.sprite.flax.flax-2", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 3), + "voxygen.voxel.sprite.flax.flax-3", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 4), + "voxygen.voxel.sprite.flax.flax-4", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + make_models( + (BlockKind::Flax, 5), + "voxygen.voxel.sprite.flax.flax-5", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::unit_z() * 0.7, + ), + // Carrot + make_models( + (BlockKind::Carrot, 0), + "voxygen.voxel.sprite.carrot.0", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 1), + "voxygen.voxel.sprite.carrot.1", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 2), + "voxygen.voxel.sprite.carrot.2", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 3), + "voxygen.voxel.sprite.carrot.3", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 4), + "voxygen.voxel.sprite.carrot.4", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Carrot, 5), + "voxygen.voxel.sprite.carrot.5", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 0), + "voxygen.voxel.sprite.tomato.0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 1), + "voxygen.voxel.sprite.tomato.1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 2), + "voxygen.voxel.sprite.tomato.2", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 3), + "voxygen.voxel.sprite.tomato.3", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Tomato, 4), + "voxygen.voxel.sprite.tomato.4", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Radish + make_models( + (BlockKind::Radish, 0), + "voxygen.voxel.sprite.radish.0", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 1), + "voxygen.voxel.sprite.radish.1", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 2), + "voxygen.voxel.sprite.radish.2", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 3), + "voxygen.voxel.sprite.radish.3", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Radish, 4), + "voxygen.voxel.sprite.radish.4", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + // Turnip + make_models( + (BlockKind::Turnip, 0), + "voxygen.voxel.sprite.turnip.turnip-0", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 1), + "voxygen.voxel.sprite.turnip.turnip-1", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 2), + "voxygen.voxel.sprite.turnip.turnip-2", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 3), + "voxygen.voxel.sprite.turnip.turnip-3", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 4), + "voxygen.voxel.sprite.turnip.turnip-4", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + make_models( + (BlockKind::Turnip, 5), + "voxygen.voxel.sprite.turnip.turnip-5", + Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), + ), + // Coconut + make_models( + (BlockKind::Coconut, 0), + "voxygen.voxel.sprite.fruit.coconut", + Vec3::new(-6.0, -6.0, 2.0), + Vec3::one(), + ), + // Scarecrow + make_models( + (BlockKind::Scarecrow, 0), + "voxygen.voxel.sprite.misc.scarecrow", + Vec3::new(-9.5, -3.0, -0.25), + Vec3::unit_z(), + ), + // Street Light + make_models( + (BlockKind::StreetLamp, 0), + "voxygen.voxel.sprite.misc.street_lamp", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::unit_z(), + ), + // Door + make_models( + (BlockKind::Door, 0), + "voxygen.voxel.sprite.door.door-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + ] + .into_iter() + .collect(); + /* let sprite_model_data = renderer + .create_model(&mesh) + .expect("Failed to upload sprite vertex to the GPU!"); */ + // println!("{:?}, {:?}", sprite_model_data.vbuf, + // sprite_model_data.vertex_range); println!("{:?}", sprite_models); + let sprite_col_lights = ShadowPipeline::create_col_lights(renderer, greedy.finalize()) + .expect("Failed to upload sprite color and light data to the GPU!"); Self { + atlas, chunks: HashMap::default(), mesh_send_tmp: send, mesh_recv: recv, mesh_todo: HashMap::default(), - sprite_models: vec![ - // Windows - ( - (BlockKind::Window1, 0), - make_models( - "voxygen.voxel.sprite.window.window-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Window2, 0), - make_models( - "voxygen.voxel.sprite.window.window-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Window3, 0), - make_models( - "voxygen.voxel.sprite.window.window-2", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Window4, 0), - make_models( - "voxygen.voxel.sprite.window.window-3", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Cacti - ( - (BlockKind::LargeCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.large_cactus", - Vec3::new(-13.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LargeCactus, 1), - make_models( - "voxygen.voxel.sprite.cacti.tall", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BarrelCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.barrel_cactus", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RoundCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.cactus_round", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.cactus_short", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MedFlatCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.flat_cactus_med", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortFlatCactus, 0), - make_models( - "voxygen.voxel.sprite.cacti.flat_cactus_short", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Fruit - ( - (BlockKind::Apple, 0), - make_models( - "voxygen.voxel.sprite.fruit.apple", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Flowers - ( - (BlockKind::BlueFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 5), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 6), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue_7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PinkFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_pink_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_red_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_red_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_red_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_white_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_white_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::YellowFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Sunflower, 0), - make_models( - "voxygen.voxel.sprite.flowers.sunflower_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Sunflower, 1), - make_models( - "voxygen.voxel.sprite.flowers.sunflower_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Grass - ( - (BlockKind::LongGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_long_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_long_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_long_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_long_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_long_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 5), - make_models( - "voxygen.voxel.sprite.grass.grass_long_6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 6), - make_models( - "voxygen.voxel.sprite.grass.grass_long_7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_med_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_med_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_med_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_med_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::MediumGrass, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_med_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_short_1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_short_2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_short_3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_short_4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShortGrass, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_short_5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 0), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 1), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 2), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 3), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 4), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 5), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 6), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 7), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 8), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 9), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-9", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 10), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-10", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Liana, 0), - make_models( - "voxygen.voxel.sprite.lianas.liana-0", - Vec3::new(-1.5, -0.5, -88.0), - Vec3::unit_z() * 0.5, - ), - ), - ( - (BlockKind::Liana, 1), - make_models( - "voxygen.voxel.sprite.lianas.liana-1", - Vec3::new(-1.0, -0.5, -55.0), - Vec3::unit_z() * 0.5, - ), - ), - ( - (BlockKind::Velorite, 0), - make_models( - "voxygen.voxel.sprite.velorite.velorite_ore", - Vec3::new(-5.0, -5.0, -5.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 0), - make_models( - "voxygen.voxel.sprite.velorite.velorite_1", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 1), - make_models( - "voxygen.voxel.sprite.velorite.velorite_2", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 2), - make_models( - "voxygen.voxel.sprite.velorite.velorite_3", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 3), - make_models( - "voxygen.voxel.sprite.velorite.velorite_4", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 4), - make_models( - "voxygen.voxel.sprite.velorite.velorite_5", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 5), - make_models( - "voxygen.voxel.sprite.velorite.velorite_6", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 6), - make_models( - "voxygen.voxel.sprite.velorite.velorite_7", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 7), - make_models( - "voxygen.voxel.sprite.velorite.velorite_8", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 8), - make_models( - "voxygen.voxel.sprite.velorite.velorite_9", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::VeloriteFrag, 9), - make_models( - "voxygen.voxel.sprite.velorite.velorite_10", - Vec3::new(-3.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 0), - make_models( - "voxygen.voxel.sprite.chests.chest", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 1), - make_models( - "voxygen.voxel.sprite.chests.chest_gold", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 2), - make_models( - "voxygen.voxel.sprite.chests.chest_dark", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Chest, 3), - make_models( - "voxygen.voxel.sprite.chests.chest_vines", - Vec3::new(-7.0, -5.0, -0.0), - Vec3::one(), - ), - ), - //Welwitch - ( - (BlockKind::Welwitch, 0), - make_models( - "voxygen.voxel.sprite.welwitch.1", - Vec3::new(-15.0, -17.0, -0.0), - Vec3::unit_z() * 0.7, - ), - ), - //Pumpkins - ( - (BlockKind::Pumpkin, 0), - make_models( - "voxygen.voxel.sprite.pumpkin.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 1), - make_models( - "voxygen.voxel.sprite.pumpkin.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 2), - make_models( - "voxygen.voxel.sprite.pumpkin.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 3), - make_models( - "voxygen.voxel.sprite.pumpkin.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 4), - make_models( - "voxygen.voxel.sprite.pumpkin.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 5), - make_models( - "voxygen.voxel.sprite.pumpkin.6", - Vec3::new(-7.0, -6.5, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pumpkin, 6), - make_models( - "voxygen.voxel.sprite.pumpkin.7", - Vec3::new(-7.0, -9.5, -0.0), - Vec3::one(), - ), - ), - //Lingonberries - ( - (BlockKind::LingonBerry, 0), - make_models( - "voxygen.voxel.sprite.lingonberry.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LingonBerry, 1), - make_models( - "voxygen.voxel.sprite.lingonberry.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LingonBerry, 2), - make_models( - "voxygen.voxel.sprite.lingonberry.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Leafy Plants - ( - (BlockKind::LeafyPlant, 0), - make_models( - "voxygen.voxel.sprite.leafy_plant.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 1), - make_models( - "voxygen.voxel.sprite.leafy_plant.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 2), - make_models( - "voxygen.voxel.sprite.leafy_plant.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 3), - make_models( - "voxygen.voxel.sprite.leafy_plant.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 4), - make_models( - "voxygen.voxel.sprite.leafy_plant.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 5), - make_models( - "voxygen.voxel.sprite.leafy_plant.6", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 6), - make_models( - "voxygen.voxel.sprite.leafy_plant.7", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 7), - make_models( - "voxygen.voxel.sprite.leafy_plant.8", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 8), - make_models( - "voxygen.voxel.sprite.leafy_plant.9", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LeafyPlant, 9), - make_models( - "voxygen.voxel.sprite.leafy_plant.10", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Ferns - ( - (BlockKind::Fern, 0), - make_models( - "voxygen.voxel.sprite.ferns.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 1), - make_models( - "voxygen.voxel.sprite.ferns.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 2), - make_models( - "voxygen.voxel.sprite.ferns.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 3), - make_models( - "voxygen.voxel.sprite.ferns.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 4), - make_models( - "voxygen.voxel.sprite.ferns.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 5), - make_models( - "voxygen.voxel.sprite.ferns.6", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 6), - make_models( - "voxygen.voxel.sprite.ferns.7", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 7), - make_models( - "voxygen.voxel.sprite.ferns.8", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 8), - make_models( - "voxygen.voxel.sprite.ferns.9", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 9), - make_models( - "voxygen.voxel.sprite.ferns.10", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 10), - make_models( - "voxygen.voxel.sprite.ferns.11", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Fern, 11), - make_models( - "voxygen.voxel.sprite.ferns.12", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Dead Bush - ( - (BlockKind::DeadBush, 0), - make_models( - "voxygen.voxel.sprite.dead_bush.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DeadBush, 1), - make_models( - "voxygen.voxel.sprite.dead_bush.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DeadBush, 2), - make_models( - "voxygen.voxel.sprite.dead_bush.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DeadBush, 3), - make_models( - "voxygen.voxel.sprite.dead_bush.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Blueberries - ( - (BlockKind::Blueberry, 0), - make_models( - "voxygen.voxel.sprite.blueberry.1", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 1), - make_models( - "voxygen.voxel.sprite.blueberry.2", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 2), - make_models( - "voxygen.voxel.sprite.blueberry.3", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 3), - make_models( - "voxygen.voxel.sprite.blueberry.4", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 4), - make_models( - "voxygen.voxel.sprite.blueberry.5", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 5), - make_models( - "voxygen.voxel.sprite.blueberry.6", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 6), - make_models( - "voxygen.voxel.sprite.blueberry.7", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 7), - make_models( - "voxygen.voxel.sprite.blueberry.8", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Blueberry, 8), - make_models( - "voxygen.voxel.sprite.blueberry.9", - Vec3::new(-6.0, -6.0, -0.0), - Vec3::one(), - ), - ), - // Ember - ( - (BlockKind::Ember, 0), - make_models( - "voxygen.voxel.sprite.ember.1", - Vec3::new(-7.0, -7.0, -2.9), - Vec3::new(1.0, 1.0, 0.0), - ), - ), - // Corn - ( - (BlockKind::Corn, 0), - make_models( - "voxygen.voxel.sprite.corn.corn-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 1), - make_models( - "voxygen.voxel.sprite.corn.corn-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 2), - make_models( - "voxygen.voxel.sprite.corn.corn-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 3), - make_models( - "voxygen.voxel.sprite.corn.corn-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 4), - make_models( - "voxygen.voxel.sprite.corn.corn-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Corn, 5), - make_models( - "voxygen.voxel.sprite.corn.corn-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Yellow Wheat - ( - (BlockKind::WheatYellow, 0), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 1), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 2), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 3), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 4), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 5), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 6), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 7), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 8), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatYellow, 9), - make_models( - "voxygen.voxel.sprite.wheat_yellow.wheat-9", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Green Wheat - ( - (BlockKind::WheatGreen, 0), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 1), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 2), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 3), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 4), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 5), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 6), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-6", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 7), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-7", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 8), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-8", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::WheatGreen, 9), - make_models( - "voxygen.voxel.sprite.wheat_green.wheat-9", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Cabbage - ( - (BlockKind::Cabbage, 0), - make_models( - "voxygen.voxel.sprite.cabbage.cabbage-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Cabbage, 1), - make_models( - "voxygen.voxel.sprite.cabbage.cabbage-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Cabbage, 2), - make_models( - "voxygen.voxel.sprite.cabbage.cabbage-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - // Flax - ( - (BlockKind::Flax, 0), - make_models( - "voxygen.voxel.sprite.flax.flax-0", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 1), - make_models( - "voxygen.voxel.sprite.flax.flax-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 2), - make_models( - "voxygen.voxel.sprite.flax.flax-2", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 3), - make_models( - "voxygen.voxel.sprite.flax.flax-3", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 4), - make_models( - "voxygen.voxel.sprite.flax.flax-4", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - ( - (BlockKind::Flax, 5), - make_models( - "voxygen.voxel.sprite.flax.flax-5", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::unit_z() * 0.7, - ), - ), - // Carrot - ( - (BlockKind::Carrot, 0), - make_models( - "voxygen.voxel.sprite.carrot.0", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 1), - make_models( - "voxygen.voxel.sprite.carrot.1", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 2), - make_models( - "voxygen.voxel.sprite.carrot.2", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 3), - make_models( - "voxygen.voxel.sprite.carrot.3", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 4), - make_models( - "voxygen.voxel.sprite.carrot.4", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Carrot, 5), - make_models( - "voxygen.voxel.sprite.carrot.5", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 0), - make_models( - "voxygen.voxel.sprite.tomato.0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 1), - make_models( - "voxygen.voxel.sprite.tomato.1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 2), - make_models( - "voxygen.voxel.sprite.tomato.2", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 3), - make_models( - "voxygen.voxel.sprite.tomato.3", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Tomato, 4), - make_models( - "voxygen.voxel.sprite.tomato.4", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Radish - ( - (BlockKind::Radish, 0), - make_models( - "voxygen.voxel.sprite.radish.0", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 1), - make_models( - "voxygen.voxel.sprite.radish.1", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 2), - make_models( - "voxygen.voxel.sprite.radish.2", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 3), - make_models( - "voxygen.voxel.sprite.radish.3", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Radish, 4), - make_models( - "voxygen.voxel.sprite.radish.4", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - // Turnip - ( - (BlockKind::Turnip, 0), - make_models( - "voxygen.voxel.sprite.turnip.turnip-0", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 1), - make_models( - "voxygen.voxel.sprite.turnip.turnip-1", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 2), - make_models( - "voxygen.voxel.sprite.turnip.turnip-2", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 3), - make_models( - "voxygen.voxel.sprite.turnip.turnip-3", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 4), - make_models( - "voxygen.voxel.sprite.turnip.turnip-4", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - ( - (BlockKind::Turnip, 5), - make_models( - "voxygen.voxel.sprite.turnip.turnip-5", - Vec3::new(-5.5, -5.5, -0.25), - Vec3::one(), - ), - ), - // Coconut - ( - (BlockKind::Coconut, 0), - make_models( - "voxygen.voxel.sprite.fruit.coconut", - Vec3::new(-6.0, -6.0, 2.0), - Vec3::one(), - ), - ), - // Scarecrow - ( - (BlockKind::Scarecrow, 0), - make_models( - "voxygen.voxel.sprite.misc.scarecrow", - Vec3::new(-9.5, -3.0, -0.25), - Vec3::unit_z(), - ), - ), - // Street Light - ( - (BlockKind::StreetLamp, 0), - make_models( - "voxygen.voxel.sprite.misc.street_lamp", - Vec3::new(-4.5, -4.5, 0.0), - Vec3::unit_z(), - ), - ), - // Door - ( - (BlockKind::Door, 0), - make_models( - "voxygen.voxel.sprite.door.door-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ] - .into_iter() - .collect(), + // sprite_model_data, + sprite_models: Arc::new(sprite_models), + sprite_col_lights, /*renderer + .create_texture_immutable_raw( + gfx::texture::Kind::D2( + tex_size.x, + tex_size.y, + gfx::texture::AaMode::Single, + ), + gfx::texture::Mipmap::Provided, + &[&tex], + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) + .expect("Failed to upload sprite color and light data to the GPU!"),*/ waves: renderer .create_texture( &assets::load_expect("voxygen.texture.waves"), @@ -1879,10 +1652,95 @@ impl Terrain { None, ) .expect("Failed to create wave texture"), + col_lights, phantom: PhantomData, } } + fn make_atlas( + renderer: &mut Renderer, + ) -> Result<(AtlasAllocator, Texture), RenderError> { + let max_texture_size = renderer.max_texture_size(); + let atlas_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); + // let atlas_size = guillotiere::Size::new(1, 1); + let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { + // TODO: Verify some good empirical constants. + small_size_threshold: 128, + large_size_threshold: 1024, + ..guillotiere::AllocatorOptions::default() + }); + // renderer.flush(); + let texture = renderer.create_texture_raw( + gfx::texture::Kind::D2( + max_texture_size, + max_texture_size, + gfx::texture::AaMode::Single, + ), + 1 as gfx::texture::Level, + // gfx::memory::Upload, + gfx::memory::Bind::SHADER_RESOURCE, /* | gfx::memory::Bind::TRANSFER_DST */ + gfx::memory::Usage::Dynamic, + (0, 0), + gfx::format::Swizzle::new(), + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + )?; + /* renderer.flush(); + let ten_millis = core::time::Duration::from_millis(10); + std::thread::sleep(ten_millis); + + renderer.update_texture( + &texture, + [0, 0], + [max_texture_size, max_texture_size], + &vec![[0u8; 4]; (usize::from(max_texture_size) * usize::from(max_texture_size))], + //&[[255u8; 4]; 64 * 64], + // NOTE: Cast is safe since the origin was a u16. + )?; + renderer.flush(); */ + // texture.cleanup(); + // Not sure if this is necessary... + // renderer.flush(); + // texture.update(); + // // FIXME: Currently, there seems to be a bug where the very first texture + // update always // fails. Not sure why, but we currently work around + // it with a dummy allocation (which we // proceed to leak, in case the + // bug can return after it's freed). let _ = atlas.allocate(guillotiere: + // :Size::new(64, 64)); + Ok((atlas, texture)) + } + + fn remove_chunk_meta(&mut self, _pos: Vec2, chunk: TerrainChunkData) { + /* println!("Terrain chunk already existed: {:?}", pos); */ + self.atlas.deallocate(chunk.col_lights); + /* let (zmin, zmax) = chunk.z_bounds; + self.z_index_up.remove(Vec3::from(zmin, pos.x, pos.y)); + self.z_index_down.remove(Vec3::from(zmax, pos.x, pos.y)); */ + } + + fn insert_chunk(&mut self, pos: Vec2, chunk: TerrainChunkData) { + if let Some(old) = self.chunks.insert(pos, chunk) { + self.remove_chunk_meta(pos, old); + } + /* let (zmin, zmax) = chunk.z_bounds; + self.z_index_up.insert(Vec3::from(zmin, pos.x, pos.y)); + self.z_index_down.insert(Vec3::from(zmax, pos.x, pos.y)); */ + } + + fn remove_chunk(&mut self, pos: Vec2) { + // println!("Terrain chunk removed: {:?}", pos); + if let Some(chunk) = self.chunks.remove(&pos) { + self.remove_chunk_meta(pos, chunk); + } + if let Some(_todo) = self.mesh_todo.remove(&pos) { + /* println!("Terrain chunk was being meshed: {:?}", + * (todo.pos, todo.started_tick)); */ + } + } + /// Maintain terrain data. To be called once per tick. pub fn maintain( &mut self, @@ -1892,9 +1750,10 @@ impl Terrain { loaded_distance: f32, view_mat: Mat4, proj_mat: Mat4, - ) { + ) -> (Aabb, Aabb, Aabb) { let current_tick = scene_data.tick; let current_time = scene_data.state.get_time(); + let mut visible_bounding_box: Option> = None; // Add any recently created or changed chunks to the list of chunks to be // meshed. @@ -1913,6 +1772,7 @@ impl Terrain { .map(|c| (false, c)), ) { + // println!("Terrain chunk change: {:?}", (modified, pos)); // TODO: ANOTHER PROBLEM HERE! // What happens if the block on the edge of a chunk gets modified? We need to // spawn a mesh worker to remesh its neighbour(s) too since their @@ -1934,6 +1794,7 @@ impl Terrain { } if neighbours { + // println!("Insert mesh_todo:: {:?}", (pos, current_tick)); self.mesh_todo.insert(pos, ChunkMeshState { pos, started_tick: current_tick, @@ -1954,6 +1815,7 @@ impl Terrain { .iter() .map(|(p, _)| *p) { + // println!("Terrain block change: {:?}", pos); let chunk_pos = scene_data.state.terrain().pos_key(pos); // Only mesh if this chunk has all its neighbors let mut neighbours = true; @@ -1967,6 +1829,7 @@ impl Terrain { } } if neighbours { + // println!("Insert mesh_todo: {:?}", (chunk_pos, current_tick)); self.mesh_todo.insert(chunk_pos, ChunkMeshState { pos: chunk_pos, started_tick: current_tick, @@ -1997,6 +1860,8 @@ impl Terrain { } } if neighbours { + // println!("Insert mesh_todo:: {:?}", (neighbour_chunk_pos, + // current_tick)); self.mesh_todo.insert(neighbour_chunk_pos, ChunkMeshState { pos: neighbour_chunk_pos, started_tick: current_tick, @@ -2009,11 +1874,13 @@ impl Terrain { } // Remove any models for chunks that have been recently removed. - for pos in &scene_data.state.terrain_changes().removed_chunks { - self.chunks.remove(pos); - self.mesh_todo.remove(pos); + for &pos in &scene_data.state.terrain_changes().removed_chunks { + self.remove_chunk(pos); } + // Limit ourselves to u16::MAX even if larger textures are supported. + let max_texture_size = renderer.max_texture_size(); + for todo in self .mesh_todo .values_mut() @@ -2048,7 +1915,9 @@ impl Terrain { * exist to avoid buggy shadow borders */ // Either this chunk or its neighbours doesn't yet exist, so we keep it in the // queue to be processed at a later date when we have its neighbours. - Err(VolGrid2dError::NoSuchChunk) => return, + Err(VolGrid2dError::NoSuchChunk) => { + continue; + }, _ => panic!("Unhandled edge case"), }; @@ -2071,13 +1940,17 @@ impl Terrain { // Queue the worker thread. let started_tick = todo.started_tick; + let sprite_models = Arc::clone(&self.sprite_models); scene_data.thread_pool.execute(move || { + let sprite_models = sprite_models; let _ = send.send(mesh_worker( pos, (min_z as f32, max_z as f32), started_tick, volume, + max_texture_size, aabb, + &sprite_models, )); }); todo.active_worker = Some(todo.started_tick); @@ -2087,17 +1960,111 @@ impl Terrain { // store it. Only pull out one chunk per frame to avoid an unacceptable // amount of blocking lag due to the GPU upload. That still gives us a // 60 chunks / second budget to play with. - if let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) { + if let Some(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)).ok() { match self.mesh_todo.get(&response.pos) { // It's the mesh we want, insert the newly finished model into the terrain model // data structure (convert the mesh to a model first of course). Some(todo) if response.started_tick <= todo.started_tick => { + let started_tick = todo.started_tick; + // println!("Finished meshing worker: (pos={:?}, response={:?}, todo={:?})", + // response.pos, response.started_tick, todo.started_tick); let load_time = self .chunks .get(&response.pos) .map(|chunk| chunk.load_time) .unwrap_or(current_time as f32); - self.chunks.insert(response.pos, TerrainChunkData { + // TODO: Allocate new atlas on allocation faillure. + let (tex, tex_size) = response.col_lights_info; + /* if self.chunks.len() == 0 { + println!("{:?}\n{:?}", tex, tex_size); + /*// HACK + let (atlas_, col_lights_) = Self::make_atlas(renderer) + .expect("Failed to create atlas texture"); + // renderer.flush(); + let ten_millis = core::time::Duration::from_millis(1000); + std::thread::sleep(ten_millis); + if let Err(err) = renderer.update_texture( + &self.col_lights, + // &col_lights, + // NOTE: Cast is safe since the origin was a u16. + [0, 0], + [1, 1], + &[[0u8; 4]], + ) { + panic!("Ahhh {:?}", err); + log::warn!("Failed to update texture: {:?}", err); + } + renderer.flush(); + std::thread::sleep(ten_millis); + self.atlas = atlas_; + self.col_lights = col_lights_; */ + } */ + let atlas = &mut self.atlas; + let allocation = atlas + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) + .expect("Not yet implemented: allocate new atlas on allocation faillure."); + // println!("Allocation {:?} for {:?} (original size = {:?}... ugh)", + // allocation, response.pos, tex_size); NOTE: Cast is safe + // since the origin was a u16. + let atlas_offs = Vec2::new( + allocation.rectangle.min.x as u16, + allocation.rectangle.min.y as u16, + ); + if atlas_offs == Vec2::zero() { + // println!("Model: {:?}", + // &response.opaque_mesh.vertices()); + // println!("Texture: {:?}", tex); + } + // let atlas_offs : Vec2 = Vec2::zero(); + /* let col_lights = renderer + .create_texture_immutable_raw( + gfx::texture::Kind::D2( + tex_size.x, + tex_size.y, + gfx::texture::AaMode::Single, + ), + gfx::texture::Mipmap::Provided, + &[&tex], + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) + .expect("Failed to upload terrain color and light data to the GPU!"); */ + /* let col_lights = renderer + .create_texture_raw( + gfx::texture::Kind::D2( + tex_size.x, + tex_size.y, + gfx::texture::AaMode::Single, + ), + 1 as gfx::texture::Level, + // gfx::memory::Upload, + gfx::memory::Bind::SHADER_RESOURCE, + gfx::memory::Usage::Dynamic, + (0, 0), + gfx::format::Swizzle::new(), + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) + .expect("Failed to upload col lights data to the GPU!"); */ + if let Err(err) = renderer.update_texture( + &self.col_lights, + // &col_lights, + // NOTE: Cast is safe since the origin was a u16. + atlas_offs.into_array(), + tex_size.into_array(), + &tex, + ) { + log::warn!("Failed to update texture: {:?}", err); + } + + self.insert_chunk(response.pos, TerrainChunkData { load_time, opaque_model: renderer .create_model(&response.opaque_mesh) @@ -2111,12 +2078,27 @@ impl Terrain { } else { None }, - shadow_model: renderer + /* shadow_model: renderer .create_model(&response.shadow_mesh) - .expect("Failed to upload chunk mesh to the GPU!"), + .expect("Failed to upload chunk mesh to the GPU!"), */ + col_lights: allocation.id,/*ShadowPipeline::create_col_lights(renderer, /*response.col_lights_info*/(tex, tex_size)) + .expect("Failed to upload chunk light-color texture to the GPU!"),*/ + /* sprite_instances: response + .sprite_instances + .into_iter() + .map(|(kind, instances)| { + ( + kind, + (renderer./*create_consts_immutable*/create_consts(&instances).expect( + "Failed to upload chunk sprite instances to the GPU!", + ), instances.len()), + ) + }) + .collect(), */ sprite_instances: response .sprite_instances .into_iter() + .map(|(kind, instances)| { ( kind, @@ -2134,33 +2116,56 @@ impl Terrain { }), ) .into_array(), + atlas_offs: Vec4::new(i32::from(atlas_offs.x), i32::from(atlas_offs.y), 0, 0).into_array(), load_time, }]) .expect("Failed to upload chunk locals to the GPU!"), - visible: false, - can_shadow: false, + visible: Visibility::OutOfRange, + can_shadow_point: false, + can_shadow_sun: false, z_bounds: response.z_bounds, frustum_last_plane_index: 0, }); - if response.started_tick == todo.started_tick { + if response.started_tick == started_tick { + // println!("Terrain chunk removed from meshing: {:?}", (response.pos, + // response.started_tick)); self.mesh_todo.remove(&response.pos); } }, // Chunk must have been removed, or it was spawned on an old tick. Drop the mesh // since it's either out of date or no longer needed. - _ => {}, + Some(_todo) => { + /* println!("Finished meshing worker: (pos={:?}, response={:?}, todo={:?})", response.pos, response.started_tick, todo.started_tick); + println!("Terrain chunk removed from meshing due to being out of date: {:?}", (response.pos, response.started_tick)); + self.mesh_todo.remove(&response.pos); */ + }, + None => {}, } } // Construct view frustum - let frustum = Frustum::from_modelview_projection((proj_mat * view_mat).into_col_arrays()); + let _all_mat = proj_mat * view_mat; + //.scaled_3d(Vec3::new(proj_mat[(0, 0)], proj_mat[(1, 1)], 1.0)); + let focus_off = focus_pos.map(|e| e.trunc()); + let frustum = Frustum::from_modelview_projection( + (proj_mat * view_mat * Mat4::translation_3d(-focus_off)).into_col_arrays(), + ); // Update chunk visibility let chunk_sz = V::RECT_SIZE.x as f32; + let scene_bounding_box: Aabb = Aabb { + min: focus_pos - 2.0, /* + * - /0.5f32 */ + max: focus_pos + 2.0, /* + 0.5f32 */ + }; + /* let mut shadow_queue = + * std::collections::VecDeque::with_capacity(self.chunks.len()); */ for (pos, chunk) in &mut self.chunks { let chunk_pos = pos.map(|e| e as f32 * chunk_sz); + chunk.can_shadow_sun = false; + // Limit focus_pos to chunk bounds and ensure the chunk is within the fog // boundary let nearest_in_chunk = Vec2::from(focus_pos).clamped(chunk_pos, chunk_pos + chunk_sz); @@ -2168,7 +2173,7 @@ impl Terrain { let in_range = distance_2 < loaded_distance.powf(2.0); if !in_range { - chunk.visible = in_range; + chunk.visible = Visibility::OutOfRange; continue; } @@ -2184,17 +2189,276 @@ impl Terrain { .coherent_test_against_frustum(&frustum, chunk.frustum_last_plane_index); chunk.frustum_last_plane_index = last_plane_index; - chunk.visible = in_frustum; + chunk.visible = if in_frustum { + Visibility::Visible + } else { + Visibility::InRange + }; + let chunk_box = Aabb { + min: Vec3::from(chunk_min), + max: Vec3::from(chunk_max), + }; + // scene_bounding_box.expand_to_contain(chunk_box); + + if in_frustum { + /* let visible_box = Aabb { + min: chunk_box.min - focus_off, + max: chunk_box.max - focus_off, + }; + let visible_box = super::fit_psr(all_mat, visible_box, |p| Vec3::from(p) / p.w/*.clamped(-p.w.abs(), p.w.abs())*/) + .map(|e| e.clamped(-1.0, 1.0)) + ; */ + let visible_box = chunk_box; + visible_bounding_box = visible_bounding_box + .map(|e| e.union(visible_box)) + .or(Some(visible_box)); + /* shadow_queue.push_back(chunk_box); + // shadow_queue.push(chunk_min + chunk_sz * 0.5); */ + } // FIXME: Hack that only works when only the lantern casts point shadows // (and hardcodes the shadow distance). Should ideally exist per-light, too. - chunk.can_shadow = distance_2 < (128.0 * 128.0); + chunk.can_shadow_point = distance_2 < (128.0 * 128.0); } + + // PSRs: potential shadow receivers + let visible_bounding_box = visible_bounding_box.unwrap_or(Aabb { + min: focus_pos - 2.0, /* + * - 0.5 */ + max: focus_pos + 2.0, /* + 0.5 */ + }); + + // PSCs: Potential shadow casters + let psc_bounding_box: Aabb = visible_bounding_box; + /*Aabb { + min: focus_pos - 0.5f32, + max: focus_pos + 0.5f32, + }; */ + let ray_direction = scene_data.get_sun_dir(); + let collides_with_aabr = |a: Aabr, b: Aabr| { + a.min.partial_cmple(&b.max).reduce_and() && a.max.partial_cmpge(&b.min).reduce_and() + }; + if ray_direction.z < 0.0 && renderer.render_mode().shadow == render::ShadowMode::Map { + let visible_bounding_box = Aabb { + min: visible_bounding_box.min - focus_off, + max: visible_bounding_box.max - focus_off, + }; + let visible_bounds_fine = Aabb { + min: visible_bounding_box.min.map(f64::from), + max: visible_bounding_box.max.map(f64::from), + }; + let inv_proj_view = (proj_mat * view_mat/* * Mat4::translation_3d(-focus_off)*/) + .map(f64::from) + .inverted(); + let visible_light_volume = super::calc_focused_light_volume_points( + inv_proj_view, + ray_direction.map(f64::from), + visible_bounds_fine, + 1e-6, + ) + .map(|v| v.map(|e| e as f32)) + .collect::>(); + + let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w())/* + focus_off*/; + let view_dir = (focus_pos.map(f32::fract)) - cam_pos; + // let new_dir: Vec3 = light_volume/*visible_light_volume*/.iter().map(|p| + // p - cam_pos).sum(); + let new_dir = view_dir; + let new_dir = new_dir.normalized(); + let dot_prod = f64::from(ray_direction.dot(new_dir)); + let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt(); + // let sin_gamma = 0.0; + let _new_dir = if sin_gamma > super::EPSILON_GAMMA { + new_dir + } else { + Vec3::from(view_mat * Vec4::from_direction(Vec3::up())).normalized() + }; + let up: Vec3 = { + /* (ray_direction) + .cross(new_dir) + .cross(ray_direction) + .normalized() */ + Vec3::up() + }; + + let ray_mat = Mat4::look_at_rh( + cam_pos, + cam_pos + ray_direction, + up, + // Vec3::up(), + ); + // println!("old: {:?} new: {:?}", visible_bounding_box, visible_light_volume); + let visible_bounds = Aabr::from(super::fit_psr( + ray_mat, + /* super::aabb_to_points(visible_bounding_box).iter().copied() */ + visible_light_volume.into_iter(), + |p| Vec3::from(p), /* / p.w */ + )); + /* let visible_bounds_old = Aabr::from(super::fit_psr(ray_mat, super::aabb_to_points(visible_bounding_box).iter().copied(), |p| Vec3::from(p) / p.w)); + println!("old: {:?} new: {:?}", visible_bounds_old, visible_bounds); */ + + self.chunks.iter_mut() + // NOTE: We deliberately avoid doing this computation for chunks we already know + // are visible, since by definition they'll always intersect the visible view + // frustum. + .filter(|chunk| chunk.1.visible == Visibility::InRange) + .for_each(|(pos, chunk)| { + let chunk_pos = pos.map(|e| e as f32 * chunk_sz); + + // Ensure the chunk is within the view frustum + let chunk_min = [chunk_pos.x, chunk_pos.y, chunk.z_bounds.0]; + let chunk_max = [ + chunk_pos.x + chunk_sz, + chunk_pos.y + chunk_sz, + chunk.z_bounds.1, + ]; + let chunk_box = Aabb { + min: Vec3::from(chunk_min) - focus_off, + max: Vec3::from(chunk_max) - focus_off, + }; + + let chunk_from_light = Aabr::from(super::fit_psr(ray_mat, super::aabb_to_points(chunk_box).iter().copied(), |p| Vec3::from(p)/* / p.w*/)); + let can_shadow_sun = collides_with_aabr(chunk_from_light, visible_bounds); + /* let can_shadow_sun_old = collides_with_aabr(chunk_from_light, visible_bounds_old); + if can_shadow_sun != can_shadow_sun_old { + println!("Different results for chunk {:?} (from light = {:?}):\n\ + old = {:?} new = {:?}", + chunk_box, chunk_from_light, can_shadow_sun_old, can_shadow_sun); + } */ + chunk.can_shadow_sun = can_shadow_sun; + }); + } + /* let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w()) + focus_off; let look_at = visible_box.center(); + let view_dir = (focus_pos - cam_pos).normalized(); + let up_vec = ray_direction.cross(view_dir).cross(light_dir).normalized(); + let sun_light_mat = Mat4::look_at_rh(look_at - ray_direction, look_at, view_dir); + let sun_bounding_box = super::fit_psr(all_mat, visible_box, |p| Vec3::from(p) / p.w/*.clamped(-p.w.abs(), p.w.abs())*/) + Aabb { + min: -0.5, + max: 0.5, + }; */ + /* if ray_direction.z < 0.0 && renderer.render_mode().shadow == render::ShadowMode::Map { + let ray = if ray_direction.x.abs() * scene_bounding_box.size().d > ray_direction.z.abs() * chunk_sz { + -ray_direction / ray_direction.x * chunk_sz + } else { + /* -ray_direction / ray_direction.z * scene_bounding_box.size().d */ + Vec3::new(0.0, 0.0, scene_bounding_box.size().d) + }; + while let Some(shadow_chunk) = shadow_queue.pop_front() { + let collides_with_aabb = |a: Aabb, b: Aabb| + a.min.partial_cmple(&b.max).reduce_and() && + a.max.partial_cmpge(&b.min).reduce_and(); + if !collides_with_aabb(scene_bounding_box, shadow_chunk) { + continue; + } + let min_chunk_pos = Vec2::from(shadow_chunk.min.map(|e| (e / chunk_sz).floor())) + .map(|e: f32| e as i32); + let max_chunk_pos = Vec2::from(shadow_chunk.max.map(|e| (e / chunk_sz).ceil())) + .map(|e: f32| e as i32); + let mut check_chunk = |x, y| { + if let Some(mut chunk) = self.chunks.get_mut(&Vec2::new(x, y)) { + let (minz, maxz) = chunk.z_bounds; + if minz <= shadow_chunk.max.z && maxz >= shadow_chunk.min.z { + chunk.can_shadow_sun = true; + // NOTE: These casts are safe because the maximum chunk index we are + // currently considering is 16384. + let x = x as f32; + let y = y as f32; + psc_bounding_box.expand_to_contain(shadow_chunk.intersection(Aabb { + min: Vec3::new(x * chunk_sz, y * chunk_sz, minz), + max: Vec3::new((x + 1.0) * chunk_sz, (y + 1.0) * chunk_sz, maxz), + })); + } + } + }; + (min_chunk_pos.y..max_chunk_pos.y).for_each(|y| { + (min_chunk_pos.x..max_chunk_pos.x).for_each(|x| { + check_chunk(x, y); + }) + }); + shadow_queue.push_back(Aabb { + min: shadow_chunk.min + ray, + max: shadow_chunk.max + ray, + }); + } + } */ + + (scene_bounding_box, visible_bounding_box, psc_bounding_box) } pub fn chunk_count(&self) -> usize { self.chunks.len() } pub fn visible_chunk_count(&self) -> usize { - self.chunks.iter().filter(|(_, c)| c.visible).count() + self.chunks + .iter() + .filter(|(_, c)| c.visible == Visibility::Visible) + .count() + } + + pub fn render_shadows( + &self, + renderer: &mut Renderer, + globals: &Consts, + shadow_mats: &Consts, + light_data: &[Light], + is_daylight: bool, + focus_pos: Vec3, + ) { + if !(renderer.render_mode().shadow == render::ShadowMode::Map) { + return; + }; + + let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { + (e as i32).div_euclid(sz as i32) + }); + + let chunk_iter = Spiral2d::new() + .filter_map(|rpos| { + let pos = focus_chunk + rpos; + self.chunks.get(&pos).map(|c| (pos, c)) + }) + .take(self.chunks.len()); + + // let is_daylight = sun_dir.z < 0.0/*0.6*/; + + // Directed shadows + if is_daylight { + for (_, chunk) in chunk_iter.clone() { + if chunk.can_shadow_sun() { + // Directed light shadows. + renderer.render_terrain_shadow_directed( + // &chunk.shadow_model, + &chunk.opaque_model, + globals, + &chunk.locals, + shadow_mats, + /* lights, + * shadows, + * &lod.map, + * &lod.horizon, */ + ); + } + } + } + + // Point shadows + for _light in light_data.iter().take(1) { + for (_, chunk) in chunk_iter.clone() { + if chunk.can_shadow_point { + // shadow_vertex_count += chunk.shadow_model.vertex_range.len(); + renderer.render_shadow_point( + &chunk.opaque_model, + // &chunk.shadow_model, + globals, + &chunk.locals, + shadow_mats, + /* lights, + * shadows, + * &lod.map, + * &lod.horizon, */ + ); + } + } + } } pub fn render( @@ -2206,6 +2470,8 @@ impl Terrain { shadow_mats: &Consts, lod: &LodData, focus_pos: Vec3, + /* sun_dir: Vec3, + * light_data: &[Light], */ ) { let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) @@ -2218,45 +2484,96 @@ impl Terrain { }) .take(self.chunks.len()); + // // Flush renderer to synchronize commands sent on the main encoder with the + // // start of the shadow encoder. + // renderer.flush(); + // Shadows + + /*scene_data.thread_pool.execute(move || { + let _ = send.send(mesh_worker( + pos, + (min_z as f32, max_z as f32), + started_tick, + volume, + max_texture_size, + aabb, + )); + });*/ // let mut shadow_vertex_count = 0; + /* let is_daylight = sun_dir.z < 0.0/*0.6*/; + + // Directed shadows for (_, chunk) in chunk_iter.clone() { - if chunk.can_shadow { - // shadow_vertex_count += chunk.shadow_model.vertex_range.len(); - renderer.render_shadow( - &chunk.shadow_model, + if is_daylight { + // Directed light shadows. + renderer.render_shadow_directed( + // &chunk.shadow_model, + &chunk.opaque_model, globals, &chunk.locals, shadow_mats, - lights, - shadows, - &lod.map, - &lod.horizon, + // lights, + // shadows, + // &lod.map, + // &lod.horizon, ); } } + // Point shadows + for _light in light_data.iter().take(1) { + for (_, chunk) in chunk_iter.clone() { + if chunk.can_shadow_point { + // shadow_vertex_count += chunk.shadow_model.vertex_range.len(); + renderer.render_shadow_point( + &chunk.opaque_model, + // &chunk.shadow_model, + globals, + &chunk.locals, + shadow_mats, + // lights, + // shadows, + // &lod.map, + // &lod.horizon, + ); + } + } + } + // Flush shadows. - renderer.flush_shadows(); + if is_daylight || light_data.len() > 0 { + renderer.flush_shadows(); + } */ // Terrain // let mut terrain_vertex_count = 0; for (_, chunk) in chunk_iter { // terrain_vertex_count += chunk.opaque_model.vertex_range.len(); - if chunk.visible { + if chunk.visible == Visibility::Visible + /* || chunk.can_shadow_sun() */ + { renderer.render_terrain_chunk( &chunk.opaque_model, // &chunk.shadow_model, + // &chunk.col_lights, + &self.col_lights, globals, &chunk.locals, lights, shadows, + shadow_mats, &lod.map, &lod.horizon, ); } } - // println!("Vertex count (shadow / terrain / ratio): {:?} / {:?} / {:?}", shadow_vertex_count, terrain_vertex_count, shadow_vertex_count as f64 / terrain_vertex_count as f64); + /* println!( + "Vertex count (shadow / terrain / ratio): {:?} / {:?} / {:?}", + shadow_vertex_count, + terrain_vertex_count, + shadow_vertex_count as f64 / terrain_vertex_count as f64 + ); */ } pub fn render_translucent( @@ -2265,14 +2582,17 @@ impl Terrain { globals: &Consts, lights: &Consts, shadows: &Consts, + shadow_mats: &Consts, lod: &LodData, focus_pos: Vec3, + cam_pos: Vec3, sprite_render_distance: f32, ) { let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) }); + // Avoid switching textures let chunk_iter = Spiral2d::new() .filter_map(|rpos| { let pos = focus_chunk + rpos; @@ -2281,34 +2601,72 @@ impl Terrain { .take(self.chunks.len()); // Terrain sprites + let chunk_size = V::RECT_SIZE.map(|e| e as f32); + let chunk_mag = (chunk_size * (f32::consts::SQRT_2 * 0.5)).magnitude_squared(); for (pos, chunk) in chunk_iter.clone() { - if chunk.visible { + if chunk.visible == Visibility::Visible { let sprite_low_detail_distance = sprite_render_distance * 0.75; let sprite_mid_detail_distance = sprite_render_distance * 0.5; let sprite_hid_detail_distance = sprite_render_distance * 0.35; let sprite_high_detail_distance = sprite_render_distance * 0.15; - let chunk_center = - pos.map2(V::RECT_SIZE, |e, sz: u32| (e as f32 + 0.5) * sz as f32); - let dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); - if dist_sqrd < sprite_render_distance.powf(2.0) { - for (kind, instances) in &chunk.sprite_instances { + let chunk_center = pos.map2(chunk_size, |e, sz| (e as f32 + 0.5) * sz); + let focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); + let dist_sqrd = + Vec2::from(cam_pos) + .distance_squared(chunk_center) + .min(Vec2::from(cam_pos).distance_squared(chunk_center - chunk_size * 0.5)) + .min(Vec2::from(cam_pos).distance_squared( + chunk_center - chunk_size.x * 0.5 + chunk_size.y * 0.5, + )) + .min( + Vec2::from(cam_pos).distance_squared(chunk_center + chunk_size.x * 0.5), + ) + .min(Vec2::from(cam_pos).distance_squared( + chunk_center + chunk_size.x * 0.5 - chunk_size.y * 0.5, + )); + if focus_dist_sqrd < sprite_render_distance.powf(2.0) { + for (kind, /*(instances, instance_count)*/instances) in (&chunk.sprite_instances).into_iter()/*.take(1)*/ { + let SpriteData { model, locals, .. } = + if sprite_config_for(kind.0).map(|config| config.wind_sway >= 0.4).unwrap_or(false) && dist_sqrd <= chunk_mag + || dist_sqrd < sprite_high_detail_distance.powf(2.0) { + &self.sprite_models[&kind][0] + } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][1] + } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][2] + } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { + &self.sprite_models[&kind][3] + } else { + &self.sprite_models[&kind][4] + }; renderer.render_sprites( - if dist_sqrd < sprite_high_detail_distance.powf(2.0) { - &self.sprite_models[&kind][0] - } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { - &self.sprite_models[&kind][1] - } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { - &self.sprite_models[&kind][2] - } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { - &self.sprite_models[&kind][3] - } else { - &self.sprite_models[&kind][4] - }, + /*Model { + vbuf: self.sprite_model_data.vbuf.clone(), + vertex_range: /*self.sprite_model_data.vertex_range()*/*/ + /* if sprite_config_for(kind.0).map(|config| config.wind_sway >= 0.4).unwrap_or(false) && dist_sqrd <= chunk_mag + || dist_sqrd < sprite_high_detail_distance.powf(2.0) { + &self.sprite_models[&kind][0].model + } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][1].model + } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][2].model + } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { + &self.sprite_models[&kind][3].model + } else { + &self.sprite_models[&kind][4].model + }/*.clone(), + }*/,*/ + model, + &self.sprite_col_lights, globals, + &chunk.locals, + locals, + // *instance_count, &instances, lights, shadows, + shadow_mats, &lod.map, &lod.horizon, ); @@ -2320,7 +2678,7 @@ impl Terrain { // Translucent chunk_iter .clone() - .filter(|(_, chunk)| chunk.visible) + .filter(|(_, chunk)| chunk.visible == Visibility::Visible) .filter_map(|(_, chunk)| { chunk .fluid_model @@ -2337,6 +2695,7 @@ impl Terrain { locals, lights, shadows, + shadow_mats, &lod.map, &lod.horizon, &self.waves, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 9f318fe94d..4fc7d4deca 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -157,6 +157,9 @@ impl PlayState for SessionState { let camera::Dependents { view_mat, cam_pos, .. } = self.scene.camera().dependents(); + let focus_pos = self.scene.camera().get_focus_pos(); + let focus_off = focus_pos.map(|e| e.trunc()); + let cam_pos = cam_pos + focus_off; // Choose a spot above the player's head for item distance checks let player_pos = match self @@ -189,7 +192,9 @@ impl PlayState for SessionState { ) }; - let cam_dir: Vec3 = Vec3::from(view_mat.inverted() * -Vec4::unit_z()); + let cam_dir: Vec3 = Vec3::from( + (view_mat/* * Mat4::translation_3d(-focus_off */).inverted() * -Vec4::unit_z(), + ); // Check to see whether we're aiming at anything let (build_pos, select_pos) = { @@ -695,34 +700,15 @@ impl PlayState for SessionState { global_state.settings.graphics.gamma = new_gamma; global_state.settings.save_to_file_warn(); }, - HudEvent::ChangeAaMode(new_aa_mode) => { + HudEvent::ChangeRenderMode(new_render_mode) => { // Do this first so if it crashes the setting isn't saved :) + println!("Changing render mode: {:?}", new_render_mode); global_state .window .renderer_mut() - .set_aa_mode(new_aa_mode) + .set_render_mode(new_render_mode) .unwrap(); - global_state.settings.graphics.aa_mode = new_aa_mode; - global_state.settings.save_to_file_warn(); - }, - HudEvent::ChangeCloudMode(new_cloud_mode) => { - // Do this first so if it crashes the setting isn't saved :) - global_state - .window - .renderer_mut() - .set_cloud_mode(new_cloud_mode) - .unwrap(); - global_state.settings.graphics.cloud_mode = new_cloud_mode; - global_state.settings.save_to_file_warn(); - }, - HudEvent::ChangeFluidMode(new_fluid_mode) => { - // Do this first so if it crashes the setting isn't saved :) - global_state - .window - .renderer_mut() - .set_fluid_mode(new_fluid_mode) - .unwrap(); - global_state.settings.graphics.fluid_mode = new_fluid_mode; + global_state.settings.graphics.render_mode = new_render_mode; global_state.settings.save_to_file_warn(); }, HudEvent::ChangeLanguage(new_language) => { @@ -736,16 +722,6 @@ impl PlayState for SessionState { localized_strings.log_missing_entries(); self.hud.update_language(localized_strings.clone()); }, - HudEvent::ChangeLightingMode(new_lighting_mode) => { - // Do this first so if it crashes the setting isn't saved :) - global_state - .window - .renderer_mut() - .set_lighting_mode(new_lighting_mode) - .unwrap(); - global_state.settings.graphics.lighting_mode = new_lighting_mode; - global_state.settings.save_to_file_warn(); - }, HudEvent::ToggleFullscreen => { global_state .window @@ -799,9 +775,8 @@ impl PlayState for SessionState { let renderer = global_state.window.renderer_mut(); - // Flush renderer to synchronize commands sent on the main encoder with the - // start of the shadow encoder. - renderer.flush(); + // Clear the shadow maps. + renderer.clear_shadows(); // Clear the screen renderer.clear(); // Render the screen using the global renderer @@ -824,6 +799,12 @@ impl PlayState for SessionState { .swap_buffers() .expect("Failed to swap window buffers!"); + /* let renderer = global_state.window.renderer_mut(); + // Clear the shadow maps. + renderer.clear_shadows(); + // Clear the screen + renderer.clear(); */ + // Wait for the next tick. clock.tick(Duration::from_millis( 1000 / global_state.settings.graphics.max_fps as u64, diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 52305495b9..2ec2562ceb 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -1,7 +1,7 @@ use crate::{ hud::{BarNumbers, CrosshairType, Intro, PressBehavior, ShortcutNumbers, XpBar}, i18n, - render::{AaMode, CloudMode, FluidMode, LightingMode}, + render::RenderMode, ui::ScaleMode, window::{GameInput, KeyMouse}, }; @@ -548,7 +548,7 @@ impl Default for Log { /// `GraphicsSettings` contains settings related to framerate and in-game /// visuals. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(default)] pub struct GraphicsSettings { pub view_distance: u32, @@ -557,34 +557,12 @@ pub struct GraphicsSettings { pub max_fps: u32, pub fov: u16, pub gamma: f32, - pub aa_mode: AaMode, - pub cloud_mode: CloudMode, - pub fluid_mode: FluidMode, - pub lighting_mode: LightingMode, + pub render_mode: RenderMode, pub window_size: [u16; 2], pub fullscreen: bool, pub lod_detail: u32, } -impl Default for GraphicsSettings { - fn default() -> Self { - Self { - view_distance: 10, - sprite_render_distance: 250, - figure_lod_render_distance: 250, - max_fps: 60, - fov: 50, - gamma: 1.0, - aa_mode: AaMode::Fxaa, - cloud_mode: CloudMode::Regular, - fluid_mode: FluidMode::Shiny, - lighting_mode: LightingMode::Ashikmin, - window_size: [1920, 1080], - fullscreen: false, - lod_detail: 500, - } - } -} #[derive(Clone, Debug, Serialize, Deserialize)] pub enum AudioOutput { /// Veloren's audio system wont work on some systems, diff --git a/voxygen/src/ui/cache.rs b/voxygen/src/ui/cache.rs index e17017b9f5..08ffacf005 100644 --- a/voxygen/src/ui/cache.rs +++ b/voxygen/src/ui/cache.rs @@ -26,7 +26,7 @@ impl Cache { let max_texture_size = renderer.max_texture_size(); let glyph_cache_dims = - Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512)); + Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); Ok(Self { glyph_cache: GlyphCache::builder() @@ -67,7 +67,7 @@ impl Cache { let max_texture_size = renderer.max_texture_size(); let cache_dims = renderer .get_resolution() - .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512)); + .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); self.glyph_cache = GlyphCache::builder() .dimensions(cache_dims.x as u32, cache_dims.y as u32) .scale_tolerance(SCALE_TOLERANCE) diff --git a/voxygen/src/ui/graphic/mod.rs b/voxygen/src/ui/graphic/mod.rs index acb3aa5be3..45d363a762 100644 --- a/voxygen/src/ui/graphic/mod.rs +++ b/voxygen/src/ui/graphic/mod.rs @@ -226,6 +226,7 @@ impl GraphicCache { // Fit into an atlas let mut loc = None; for (atlas_idx, (ref mut atlas, _)) in self.atlases.iter_mut().enumerate() { + let dims = dims.map(|e| e.max(1)); if let Some(rectangle) = atlas.allocate(size2(i32::from(dims.x), i32::from(dims.y))) { let aabr = aabr_from_alloc_rect(rectangle); @@ -239,6 +240,7 @@ impl GraphicCache { // Create a new atlas None => { let (mut atlas, texture) = create_atlas_texture(renderer); + let dims = dims.map(|e| e.max(1)); let aabr = atlas .allocate(size2(i32::from(dims.x), i32::from(dims.y))) .map(aabr_from_alloc_rect) @@ -314,7 +316,7 @@ fn create_atlas_texture(renderer: &mut Renderer) -> (SimpleAtlasAllocator, Textu let size = Vec2::new(w, h).map(|e| { (e * GRAPHIC_CACHE_RELATIVE_SIZE) .max(512) - .min(max_texture_size as u16) + .min(max_texture_size) }); let atlas = SimpleAtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))); diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 4fcd0cb170..dd76b49506 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -664,6 +664,13 @@ impl Ui { } else { [p2.into_array(), p1.into_array(), p3.into_array()] }; + /* // If triangle is counter-clockwise, reverse it. + let (v1, v2): (Vec3, Vec3) = ((p2 - p1).into(), (p3 - p1).into()); + let triangle = if v1.cross(v2).z > 0.0 { + [p2.into_array(), p1.into_array(), p3.into_array()] + } else { + [p1.into_array(), p2.into_array(), p3.into_array()] + }; */ mesh.push_tri(create_ui_tri( triangle, [[0.0; 2]; 3], diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 7f80fe6f2f..4b089d767e 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -450,10 +450,7 @@ impl Window { factory, win_color_view, win_depth_view, - settings.graphics.aa_mode, - settings.graphics.cloud_mode, - settings.graphics.fluid_mode, - settings.graphics.lighting_mode, + settings.graphics.render_mode, )?, window, cursor_grabbed: false, diff --git a/world/Cargo.toml b/world/Cargo.toml index 24c424845f..207f1a1ee1 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -11,7 +11,7 @@ bitvec = "0.17.4" fxhash = "0.2.1" image = "0.22.3" itertools = "0.8.2" -vek = "0.10.0" +vek = { version = "0.11.2", features = ["serde"] } noise = { version = "0.6.0", default-features = false } num = "0.2.0" ordered-float = "1.0" @@ -29,5 +29,6 @@ serde_derive = "1.0.102" ron = "0.5.1" [dev-dependencies] +criterion = "0.3" pretty_env_logger = "0.3.0" minifb = "0.14.0" diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 1c12ac383f..4e636d20b3 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -72,6 +72,8 @@ impl Civs { pub fn generate(seed: u32, sim: &mut WorldSim) -> Self { let mut this = Self::default(); let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + // let path_rng = RandomField::new(rng.gen()); + let mut ctx = GenCtx { sim, rng }; for _ in 0..INITIAL_CIV_COUNT { @@ -81,6 +83,30 @@ impl Civs { } } + /* { + let v = (0..sim.chunks.len() as i32).collect::>(); + // Find edge wewghts. + let e = (0..sim.chunks.len() as i32) + .into_par_iter() + .flat_map(|posi| { + let pos = uniform_idx_as_vec2(posi); + NEIGHBORS + .iter() + .enumerate() + .filter_map(|(idx, dir)| walk_in_dir(sim, pos, dir).map(|w| (w, (posi * NEIGHBORS.len() + idx) as i32))) + }) + .collect::>(); + // Connect all civiizations with paths simultaneously using a minimum spanning tree. + let mst = par_boruvka( + v, e, + |u, v| vec2_as_uniform_idx(u, v) as i32, + |posi| uniform_idx_as_vec2(posi).x, + |posi| uniform_idx_as_vec2(posi).y, + ); + // Add path connections for all edges. + G + } */ + for _ in 0..INITIAL_CIV_COUNT * 3 { attempt(5, || { let loc = find_site_loc(&mut ctx, None)?; @@ -440,6 +466,62 @@ impl Civs { Some(site) } + /* fn write_paths(&mut self, ctx: &mut GenCtx, path_rng: &Vec2, paths: impl Iterator<(i32, i32, usize)>) { + paths.for_each(|(i, j, neighbor)| { + let neighbor = neighbor & 7; + let to_neighbor_dir = NEIGHBORS[neighbor]; + let from_neighbor_dir = NEIGHBORS[7 - neighbor]; + let from_idx = Vec2::new(i, j); + let to_idx = from_idx + to_neighbor_dir; + let from_chunk = ctx.sim.get_mut(from_idx).unwrap(); + from_chunk.path.neighbors |= 1 << to_neighbor_dir; + from_chunk.path.offset = path_rng.map(|rng| (rng.get(from_idx) & 31) as f32); + let to_chunk = ctx.sim.get_mut(to_idx).unwrap(); + to_chunk.path.neighbors |= 1 << from_neighbor_dir; + to_chunk.path.offset = path_rng.map(|rng| (rng.get(to_idx) & 31) as f32); + + // from_idx.path.neighbors |= 1 << ((to_prev_idx as u8 + 4) % 8); + // ctx.sim.get_mut(locs[2]).unwrap().path.neighbors |= + // 1 << ((to_next_idx as u8 + 4) % 8); + // let mut chunk = ctx.sim.get_mut(locs[1]).unwrap(); + // TODO: Split out so we don't run these repeatedly on the same chunk. + // to_idx.path.offset = path_rng.map(|rng| (rng.get(to_idx) & 31) as f32); + }); + /* for locs in path.nodes().windows(3) { + let to_prev_idx = NEIGHBORS + .iter() + .enumerate() + .find(|(_, dir)| **dir == locs[0] - locs[1]) + .expect("Track locations must be neighbors") + .0; + let to_next_idx = NEIGHBORS + .iter() + .enumerate() + .find(|(_, dir)| **dir == locs[2] - locs[1]) + .expect("Track locations must be neighbors") + .0; + + ctx.sim.get_mut(locs[0]).unwrap().path.neighbors |= + 1 << ((to_prev_idx as u8 + 4) % 8); + ctx.sim.get_mut(locs[2]).unwrap().path.neighbors |= + 1 << ((to_next_idx as u8 + 4) % 8); + let mut chunk = ctx.sim.get_mut(locs[1]).unwrap(); + chunk.path.neighbors |= + (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); + chunk.path.offset = Vec2::new( + ctx.rng.gen_range(-16.0, 16.0), + ctx.rng.gen_range(-16.0, 16.0), + ); + } */ + + /* // Take note of the track + let track = self.tracks.insert(Track { cost, path }); + self.track_map + .entry(site) + .or_default() + .insert(nearby, track); */ + } */ + fn tick(&mut self, _ctx: &mut GenCtx, years: f32) { for site in self.sites.iter_mut() { site.simulate(years, &self.places.get(site.place).nat_res); diff --git a/world/src/lib.rs b/world/src/lib.rs index 6f47c7a311..d815e23795 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -14,9 +14,9 @@ pub mod util; // Reexports pub use crate::config::CONFIG; +pub use block::BlockGen; use crate::{ - block::BlockGen, column::{ColumnGen, ColumnSample}, util::{Grid, Sampler}, }; diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index 9ab6f91dce..89d1e80da2 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -1,3 +1,4 @@ +// pub mod boruvka; pub mod fast_noise; pub mod grid; pub mod random;