diff --git a/.gitlab/CI/build.gitlab-ci.yml b/.gitlab/CI/build.gitlab-ci.yml index 1683d4c7b1..011dda3f81 100644 --- a/.gitlab/CI/build.gitlab-ci.yml +++ b/.gitlab/CI/build.gitlab-ci.yml @@ -20,15 +20,15 @@ benchmarks: retry: max: 2 -# Coverage is needed on master for the README.md badge to work -coverage: - extends: .recompile - stage: build - script: - - ln -s /dockercache/cache-tarpaulin target - - cargo tarpaulin -v - retry: - max: 2 +# # Coverage is needed on master for the README.md badge to work +# coverage: +# extends: .recompile +# stage: build +# script: +# - ln -s /dockercache/cache-tarpaulin target +# - cargo tarpaulin -v +# retry: +# max: 2 #linux, windows, macos builds here as template .tlinux: diff --git a/CHANGELOG.md b/CHANGELOG.md index fd43ba4018..55245f8670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- New level of detail feature, letting you see all the world's terrain at any view distance. +- Point and directional lights now cast realistic shadows, using shadow mapping. + ### Changed - Fixed a bug where leaving the Settings menu by pressing "N" in single player kept the game paused +- The world map has been refactored to support arbitrary sizes and compute horizon maps. +- Veloren's lighting has been completely overhauled. +- The graphics options were made much more flexible and configurable. +- Many shader optimizations. +- Voxel model creation was switched to use greedy meshing, improving performance. +- Animation and terrain math were switched to use SIMD where possible, improving performance. +- The way we cache glyphs was refactored, fixed, and optimized. +- Colors for models and figures were adjusted to account for the saturation hack. ### Removed +- MSAA has been removed due to incompatibility with greedy meshing. +- Removed a saturation hack that led to colors being improperly displayed. + ## [0.7.0] - 2020-08-15 ### Added diff --git a/Cargo.lock b/Cargo.lock index 85274aa25a..259543ab2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,6 +381,12 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +[[package]] +name = "bytemuck" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7a1029718df60331e557c9e83a55523c955e5dd2a7bfeffad6bbd50b538ae9" + [[package]] name = "byteorder" version = "0.5.3" @@ -581,7 +587,7 @@ dependencies = [ [[package]] name = "conrod_core" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" +source = "git+https://gitlab.com/veloren/conrod.git#09d4675c4549b0fa5b6a87eacf6e1851876a80b8" dependencies = [ "conrod_derive", "copypasta", @@ -596,7 +602,7 @@ dependencies = [ [[package]] name = "conrod_derive" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" +source = "git+https://gitlab.com/veloren/conrod.git#09d4675c4549b0fa5b6a87eacf6e1851876a80b8" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", @@ -606,7 +612,7 @@ dependencies = [ [[package]] name = "conrod_winit" version = "0.63.0" -source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30" +source = "git+https://gitlab.com/veloren/conrod.git#09d4675c4549b0fa5b6a87eacf6e1851876a80b8" [[package]] name = "const-random" @@ -1045,9 +1051,9 @@ checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "deflate" -version = "0.7.20" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" dependencies = [ "adler32", "byteorder 1.3.4", @@ -1224,30 +1230,18 @@ name = "euc" version = "0.5.1" source = "git+https://github.com/zesterer/euc.git#c9a7c17a03d45fce00caeeca09afa1e1558cd183" dependencies = [ - "vek", + "vek 0.11.2", ] [[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", ] -[[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.8" @@ -1878,8 +1872,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", @@ -2075,13 +2070,14 @@ dependencies = [ [[package]] name = "image" -version = "0.22.5" +version = "0.23.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed2ada878397b045454ac7cfb011d73132c59f31a955d230bd1f1c2e68eb4a" +checksum = "543904170510c1b5fb65140485d84de4a57fddb2ed685481e9020ce3d2c9f64c" dependencies = [ + "bytemuck", "byteorder 1.3.4", "num-iter", - "num-rational", + "num-rational 0.3.0", "num-traits", "png", ] @@ -2095,15 +2091,6 @@ dependencies = [ "autocfg 1.0.0", ] -[[package]] -name = "inflate" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" -dependencies = [ - "adler32", -] - [[package]] name = "inotify" version = "0.8.3" @@ -2471,6 +2458,15 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + [[package]] name = "mio" version = "0.6.22" @@ -2686,7 +2682,7 @@ dependencies = [ "num-complex", "num-integer", "num-iter", - "num-rational", + "num-rational 0.2.4", "num-traits", ] @@ -2744,6 +2740,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.12" @@ -3091,14 +3098,14 @@ dependencies = [ [[package]] name = "png" -version = "0.15.3" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef859a23054bbfee7811284275ae522f0434a3c8e7f4b74bd4a35ae7e1c4a283" +checksum = "dfe7f9f1c730833200b134370e1d5098964231af8450bce9b78ee3ab5278b970" dependencies = [ "bitflags", "crc32fast", "deflate", - "inflate", + "miniz_oxide", ] [[package]] @@ -3553,9 +3560,9 @@ dependencies = [ [[package]] name = "roots" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c67c712ab62be58b24ab8960e2b95dd4ee00aac115c76f2709657821fe376d" +checksum = "84348444bd7ad45729d0c49a4240d7cdc11c9d512c06c5ad1835c1ad4acda6db" [[package]] name = "route-recognizer" @@ -3778,6 +3785,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "sha1" version = "0.6.0" @@ -4043,9 +4061,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" @@ -4440,7 +4458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa14b9f5cd7d513bab5accebe8f403df8b1ac22302cac549a6ac99c0a007c84a" dependencies = [ "num-traits", - "vek", + "vek 0.11.2", ] [[package]] @@ -4608,6 +4626,19 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "vek" +version = "0.12.0" +source = "git+https://gitlab.com/veloren/vek.git?branch=fix_intrinsics#237a78528b505f34f6dde5dc77db3b642388fe4a" +dependencies = [ + "approx", + "num-integer", + "num-traits", + "rustc_version", + "serde", + "static_assertions", +] + [[package]] name = "veloren-chat-cli" version = "0.7.0" @@ -4629,11 +4660,13 @@ dependencies = [ "futures-util", "hashbrown", "image", + "num 0.2.1", "num_cpus", + "rayon", "specs", "tracing", "uvth 3.1.1", - "vek", + "vek 0.12.0", "veloren-common", "veloren_network", ] @@ -4658,14 +4691,16 @@ dependencies = [ "rand 0.7.3", "rayon", "ron", + "roots", "serde", "serde_json", + "serde_repr", "slab", "specs", "specs-idvs", "sum_type", "tracing", - "vek", + "vek 0.12.0", ] [[package]] @@ -4696,7 +4731,7 @@ dependencies = [ "tiny_http", "tracing", "uvth 3.1.1", - "vek", + "vek 0.12.0", "veloren-common", "veloren-world", "veloren_network", @@ -4733,6 +4768,7 @@ dependencies = [ "failure", "gfx", "gfx_device_gl", + "gfx_gl", "gilrs", "git2", "glsl-include", @@ -4757,7 +4793,7 @@ dependencies = [ "tracing-subscriber", "treeculler", "uvth 3.1.1", - "vek", + "vek 0.12.0", "veloren-client", "veloren-common", "veloren-server", @@ -4776,7 +4812,7 @@ dependencies = [ "libloading 0.6.2", "notify", "tracing", - "vek", + "vek 0.12.0", "veloren-common", ] @@ -4787,6 +4823,7 @@ dependencies = [ "arr_macro", "bincode", "bitvec", + "criterion", "fxhash", "hashbrown", "image", @@ -4801,11 +4838,10 @@ dependencies = [ "rand_chacha 0.2.2", "rayon", "ron", - "roots", "serde", "tracing", "tracing-subscriber", - "vek", + "vek 0.12.0", "veloren-common", ] diff --git a/Cargo.toml b/Cargo.toml index e73d2cd0a8..505bbcb4af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,9 @@ opt-level = 2 [profile.no_overflow.package."veloren-world"] opt-level = 3 +[profile.no_overflow.package."veloren-voxygen-anim"] +opt-level = 3 + # this profile is used by developers if dev doesn't has enough debug information, the name must != debug, as debug is used by dev because.... [profile.debuginfo] inherits= 'dev' @@ -74,6 +77,7 @@ debug = false inherits = 'release' debug = 1 -# cpal conflict fix isn't released yet [patch.crates-io] +# cpal conflict fix isn't released yet winit = { git = "https://github.com/Imberflur/winit.git", branch = "macos-test" } +vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics" } diff --git a/assets/common/npc_names.json b/assets/common/npc_names.json index 1491b3f0b6..d24db1222a 100644 --- a/assets/common/npc_names.json +++ b/assets/common/npc_names.json @@ -598,6 +598,34 @@ } } }, + "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 + }, "quadruped_low": { "body": { "keyword": "quadruped_low", diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE.ron index bf96a3e9b5..04e6bffe0f 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE.ron @@ -303,6 +303,15 @@ magischen Gegenstände ergattern?"#, "hud.settings.bit_depth": "Bittiefe", "hud.settings.refresh_rate": "Bildwiederholrate", "hud.settings.fullscreen": "Vollbild", + "hud.settings.lighting_rendering_mode": "Beleuchtung", + "hud.settings.lighting_rendering_mode.ashikhmin": "Typ A", + "hud.settings.lighting_rendering_mode.blinnphong": "Typ B", + "hud.settings.lighting_rendering_mode.lambertian": "Typ L", + "hud.settings.shadow_rendering_mode": "Schatten", + "hud.settings.shadow_rendering_mode.none": "Keine Schatten", + "hud.settings.shadow_rendering_mode.cheap": "Gering", + "hud.settings.shadow_rendering_mode.map": "Voll", + "hud.settings.lod_detail": "LoD Detail", "hud.settings.save_window_size": "Größe speichern", "hud.settings.music_volume": "Musik Lautstärke", diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 6eb69f292f..a499419b08 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -304,6 +304,18 @@ magically infused items?"#, "hud.settings.refresh_rate": "Refresh Rate", "hud.settings.fullscreen": "Fullscreen", "hud.settings.save_window_size": "Save window size", + "hud.settings.lighting_rendering_mode": "Lighting Rendering Mode", + "hud.settings.lighting_rendering_mode.ashikhmin": "Type A", + "hud.settings.lighting_rendering_mode.blinnphong": "Type B", + "hud.settings.lighting_rendering_mode.lambertian": "Type L", + "hud.settings.shadow_rendering_mode": "Shadow Rendering Mode", + "hud.settings.shadow_rendering_mode.none": "None", + "hud.settings.shadow_rendering_mode.cheap": "Cheap", + "hud.settings.shadow_rendering_mode.map": "Map", + "hud.settings.shadow_rendering_mode.map.resolution": "Resolution", + "hud.settings.lod_detail": "LoD Detail", + "hud.settings.save_window_size": "Save window size", + "hud.settings.music_volume": "Music Volume", "hud.settings.sound_effect_volume": "Sound Effects Volume", diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index 26d85fea88..315807549a 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -1,16 +1,56 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #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; @@ -18,6 +58,7 @@ uniform u_locals { struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -27,41 +68,155 @@ uniform u_bones { #include #include -#include +#include out vec4 tgt_color; void main() { - vec3 light, diffuse_light, ambient_light; - 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 *= point_shadow; - ambient_light *= point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; + // 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; - float ao = pow(f_ao, 0.5) * 0.85 + 0.15; + // 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; - ambient_light *= ao; - diffuse_light *= ao; + // 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 surf_color = illuminate(srgb_to_linear(model_col.rgb * f_col), light, diffuse_light, ambient_light); + // 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); */ + // 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); +#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; + + // 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; + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // 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(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 = f_ao * sqrt(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; + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + // get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, surf_color * f_light * point_shadow, 0.5 * surf_color * f_light * point_shadow, 0.5 * surf_color * f_light * point_shadow, 2.0, emitted_light, reflected_light); + + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // diffuse_light *= point_shadow; + // ambient_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 = 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(normalize(f_pos - cam_pos.xyz), 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; - } - } + // // 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 e39718670b..43a9f5decc 100644 --- a/assets/voxygen/shaders/figure-vert.glsl +++ b/assets/voxygen/shaders/figure-vert.glsl @@ -1,16 +1,34 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include +#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; @@ -18,6 +36,7 @@ uniform u_locals { struct BoneData { mat4 bone_mat; + mat4 normals_mat; }; layout (std140) @@ -26,38 +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 = 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); - gl_Position = all_mat * vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // // 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/*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 = 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 e56796d1d5..1b876d678d 100644 --- a/assets/voxygen/shaders/fluid-frag/cheap.glsl +++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl @@ -1,17 +1,47 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + #include #include 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; @@ -20,37 +50,149 @@ out vec4 tgt_color; #include #include +#include void main() { - // First 3 normals are negative, next 3 are positive - 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) ); + // 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]; - vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // 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 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); - float point_shadow = shadow_at(f_pos,f_norm); - 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; - vec3 surf_color = srgb_to_linear(vec3(0.4, 0.7, 2.0)) * light * diffuse_light * ambient_light; + 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) * 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)); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.25, true, clouds); + /* 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 - float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag); +#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); +#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; - vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 1.0 / (1.0 + diffuse_light)), passthrough); + // 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*/); - tgt_color = mix(color, vec4(fog_color, 0.0), 0.0); + float fluid_alt = f_pos.z;//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 / sqrt(2.0)*/; + const float n2 = 1.3325; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < fluid_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + // Water is transparent so both normals are valid. + vec3 cam_norm = faceforward(f_norm, f_norm, cam_to_frag); + + vec3 mu = MU_WATER; + // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. + vec3 cam_attenuation = vec3(1.0);//compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); + + // NOTE: Assumes normal is vertical. + vec3 sun_view_dir = cam_pos.z <= fluid_alt ? /*refract(view_dir, -f_norm, 1.0 / n2)*//*reflect(view_dir, -f_norm)*/-view_dir : view_dir;//vec3(view_dir.xy, -view_dir.z) : view_dir; + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + 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; + // Squared to account for prior saturation. + // 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)); + float max_light = 0.0; + 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; + + // 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, 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 = 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); + // diffuse_light_point -= specular_light_point; + + // 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 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)); + + 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); + +#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 f6692cbeb9..90402d2065 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -1,17 +1,49 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + +// https://www.shadertoy.com/view/XdsyWf + #include #include 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; @@ -20,6 +52,7 @@ out vec4 tgt_color; #include #include +#include vec3 warp_normal(vec3 norm, vec3 pos, float time) { return normalize(norm @@ -68,7 +101,20 @@ 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*/); + vec3 view_dir = -cam_to_frag; float frag_dist = length(f_pos - cam_pos.xyz); vec3 b_norm; @@ -81,9 +127,10 @@ void main() { } vec3 c_norm = cross(f_norm, b_norm); - float wave00 = wave_height(f_pos); - float wave10 = wave_height(f_pos + vec3(0.1, 0, 0)); - float wave01 = wave_height(f_pos + vec3(0, 0.1, 0)); + vec3 wave_pos = f_pos + focus_off.xyz; + float wave00 = wave_height(wave_pos); + float wave10 = wave_height(wave_pos + vec3(0.1, 0, 0)); + float wave01 = wave_height(wave_pos + vec3(0, 0.1, 0)); float slope = abs(wave00 - wave10) * abs(wave00 - wave01); vec3 nmap = vec3( @@ -95,32 +142,190 @@ void main() { nmap = mix(f_norm, normalize(nmap), min(1.0 / pow(frag_dist, 0.75), 1)); vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y; + // vec3 norm = f_norm; - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(norm, time_of_day.x, light, diffuse_light, ambient_light, 0.0); - float point_shadow = shadow_at(f_pos, norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, norm); - light += point_light; - diffuse_light += point_light; + vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER; +#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 fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); - vec4 clouds; - vec3 fog_color = get_sky_color(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.25, true, clouds); + 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)*/; + const float n2 = 1.3325; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < fluid_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); - vec3 reflect_ray_dir = reflect(cam_to_frag, norm); + // Water is transparent so both normals are valid. + vec3 cam_norm = faceforward(norm, norm, cam_to_frag); + vec4 _clouds; + vec3 reflect_ray_dir = reflect(cam_to_frag/*-view_dir*/, norm); + vec3 refract_ray_dir = refract(cam_to_frag/*-view_dir*/, norm, 1.0 / n2); + vec3 sun_view_dir = view_dir;///*sign(cam_pos.z - fluid_alt) * view_dir;*/cam_pos.z <= fluid_alt ? -view_dir : view_dir; + // vec3 sun_view_dir = cam_pos.z <= fluid_alt ? -view_dir : view_dir; + vec3 beam_view_dir = reflect_ray_dir;//cam_pos.z <= fluid_alt ? -refract_ray_dir : reflect_ray_dir; + /* vec4 reflect_ray_dir4 = view_mat * vec4(reflect_ray_dir, 1.0); + 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 = 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, true, _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)); + // NOTE: Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. + // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water + // /*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); */ +#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); +#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; + + // 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 + // along the the ray from a to b where it intersects with the surface plane; if it doesn't, + // then the whole segment from a to b is considered underwater. + // TODO: Consider doing for point lights. + // vec3 cam_surface_dir = faceforward(vec3(0.0, 0.0, 1.0), cam_to_frag, vec3(0.0, 0.0, 1.0)); + + // vec3 water_intersection_surface_camera = vec3(cam_pos); + // bool _water_intersects_surface_camera = IntersectRayPlane(f_pos, view_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), cam_surface_dir, water_intersection_surface_camera); + // // Should work because we set it up so that if IntersectRayPlane returns false for camera, its default intersection point is cam_pos. + // float water_depth_to_camera = length(water_intersection_surface_camera - f_pos); + + // vec3 water_intersection_surface_light = f_pos; + // bool _light_intersects_surface_water = IntersectRayPlane(f_pos, sun_dir.z <= 0.0 ? sun_dir : moon_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), vec3(0.0, 0.0, 1.0), water_intersection_surface_light); + // // Should work because we set it up so that if IntersectRayPlane returns false for light, its default intersection point is f_pos-- + // // i.e. if a light ray can't hit the water, it shouldn't contribute to coloring at all. + // float water_depth_to_light = length(water_intersection_surface_light - f_pos); + + // // For ambient color, we just take the distance to the surface out of laziness. + // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); + + // // Color goes down with distance... + // // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law. + // vec3 water_color_direct = exp(-MU_WATER);//exp(-MU_WATER);//vec3(1.0); + // vec3 water_color_direct = exp(-water_attenuation * (water_depth_to_light + water_depth_to_camera)); + // vec3 water_color_ambient = exp(-water_attenuation * (water_depth_to_vertical + water_depth_to_camera)); + vec3 mu = MU_WATER; + // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water. + vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); + // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); + // For ambient color, we just take the distance to the surface out of laziness. + // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law. + // float water_depth_to_vertical = max(fluid_alt - cam_pos.z/*f_light*/, 0.0); + // vec3 ambient_attenuation = exp(-mu * water_depth_to_vertical); + + // For ambient reflection, we just take the water + + vec3 k_a = vec3(1.0); + // Oxygen is light blue. + vec3 k_d = vec3(/*vec3(0.2, 0.9, 0.99)*/1.0); + vec3 k_s = vec3(R_s);//2.0 * reflect_color; + + vec3 emitted_light, reflected_light; + // vec3 light, diffuse_light, ambient_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); + // 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(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); + + // vec3 dump_light = vec3(0.0); + // vec3 specular_light_point = vec3(0.0); + // lights_at(f_pos, cam_norm, view_dir, mu, cam_attenuation, fluid_alt, vec3(0.0), vec3(0.0), /*vec3(1.0)*/k_s, alpha, dump_light, specular_light_point); + // 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, 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. + // reflected_light += k_d * (diffuse_light_point/* + f_light * point_shadow * shade_frac*/) + /*water_color_ambient*/specular_light_point; + + /* vec3 point_light = light_at(f_pos, norm); + emitted_light += point_light; + reflected_light += point_light; */ + + // get_sun_diffuse(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, norm); + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + // 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, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * reflected_light/* * log(1.0 - MU_WATER)*/); + + // 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) : + vec3(0.0, 1.0, 1.0) + ; */ + // passthrough = passthrough * length(cam_attenuation); + + // vec3 reflect_ray_dir = reflect(cam_to_frag, norm); // Hack to prevent the reflection ray dipping below the horizon and creating weird blue spots in the water - reflect_ray_dir.z = max(reflect_ray_dir.z, 0.01); + // reflect_ray_dir.z = max(reflect_ray_dir.z, 0.01); - vec4 _clouds; - vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.25, false, _clouds) * f_light; + // vec4 _clouds; + // vec3 reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.25, false, _clouds) * f_light; // Tint - reflect_color = reflect_color * 0.5 * (diffuse_light + ambient_light); + // reflect_color = mix(reflect_color, surf_color, 0.6); + + // vec4 color = mix(vec4(reflect_color * 2.0, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*/(f_light * point_shadow + point_light) * 0.25)), passthrough); + // vec4 color = mix(vec4(reflect_color * 2.0, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*/(/*f_light * point_shadow*/f_light * point_shadow + reflected_light_point/* + point_light*//*reflected_light*/) * 0.25)), passthrough); + // vec4 color = mix(vec4(surf_color, 1.0), vec4(surf_color, 0.0), passthrough); + //vec4 color = vec4(surf_color, 1.0); + // vec4 color = mix(vec4(reflect_color, 1.0), vec4(surf_color, 1.0 / (1.0 + /*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point/* + point_light*//*reflected_light*/))), passthrough); + + // float log_cam = log(min(cam_attenuation.r, min(cam_attenuation.g, cam_attenuation.b))); + float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); + vec4 color = vec4(surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam))); + // vec4 color = vec4(surf_color, mix(1.0, 1.0 / (1.0 + /*0.25 * *//*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point)), passthrough)); + // vec4 color = vec4(surf_color, mix(1.0, length(cam_attenuation), passthrough)); + + /* reflect_color = reflect_color * 0.5 * (diffuse_light + ambient_light); // 0 = 100% reflection, 1 = translucent water float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag); - vec4 color = mix(vec4(reflect_color, 1.0), vec4(vec3(0), 1.0 / (1.0 + diffuse_light * 0.25)), passthrough); + 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 52907b1ef6..6802ab11b9 100644 --- a/assets/voxygen/shaders/fluid-vert.glsl +++ b/assets/voxygen/shaders/fluid-vert.glsl @@ -1,46 +1,83 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + #include #include +#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; -flat out vec3 f_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 -= 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.z -= 0.1 + 0.1 * (sin(tick.x * 2.0 + f_pos.x * 2.0 + f_pos.y * 2.0) + 1.0) * 0.5; + // 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; +#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); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + 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); } diff --git a/assets/voxygen/shaders/include/cloud/none.glsl b/assets/voxygen/shaders/include/cloud/none.glsl index a20fb82326..917fd5e60c 100644 --- a/assets/voxygen/shaders/include/cloud/none.glsl +++ b/assets/voxygen/shaders/include/cloud/none.glsl @@ -1,5 +1,3 @@ -uniform sampler2D t_noise; - vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, float quality) { return vec4(0.0); } diff --git a/assets/voxygen/shaders/include/cloud/regular.glsl b/assets/voxygen/shaders/include/cloud/regular.glsl index a40c700e01..6e9ef6d947 100644 --- a/assets/voxygen/shaders/include/cloud/regular.glsl +++ b/assets/voxygen/shaders/include/cloud/regular.glsl @@ -1,8 +1,5 @@ -uniform sampler2D t_noise; +#include -const float CLOUD_AVG_HEIGHT = 1025.0; -const float CLOUD_HEIGHT_MIN = CLOUD_AVG_HEIGHT - 60.0; -const float CLOUD_HEIGHT_MAX = CLOUD_AVG_HEIGHT + 60.0; const float CLOUD_THRESHOLD = 0.27; const float CLOUD_SCALE = 5.0; const float CLOUD_DENSITY = 100.0; @@ -11,7 +8,15 @@ float vsum(vec3 v) { return v.x + v.y + v.z; } +vec3 get_cloud_heights() { + float CLOUD_AVG_HEIGHT = /*1025.0*/view_distance.z + 0.7 * view_distance.w; + float CLOUD_HEIGHT_MIN = CLOUD_AVG_HEIGHT - 60.0; + float CLOUD_HEIGHT_MAX = CLOUD_AVG_HEIGHT + 60.0; + return vec3(CLOUD_AVG_HEIGHT, CLOUD_HEIGHT_MIN, CLOUD_HEIGHT_MAX); +} + vec2 cloud_at(vec3 pos) { + vec3 max_heights = get_cloud_heights(); vec2 scaled_pos = pos.xy / CLOUD_SCALE; float tick_offs = 0.0 @@ -29,10 +34,10 @@ vec2 cloud_at(vec3 pos) { + texture(t_noise, scaled_pos * 0.02 + tick_offs + time_of_day.x * 0.0002).x * 0.15 ) * value; - float density = max((value - CLOUD_THRESHOLD) - abs(pos.z - CLOUD_AVG_HEIGHT) / 200.0, 0.0) * CLOUD_DENSITY; + float density = max((value - CLOUD_THRESHOLD) - abs(pos.z - max_heights.x) / 200.0, 0.0) * CLOUD_DENSITY; - const float SHADE_GRADIENT = 1.5 / (CLOUD_AVG_HEIGHT - CLOUD_HEIGHT_MIN); - float shade = (pos.z - CLOUD_AVG_HEIGHT) / (CLOUD_HEIGHT_MAX - CLOUD_HEIGHT_MIN) * 5.0 + 0.3; + float SHADE_GRADIENT = 1.5 / (max_heights.x - max_heights.y); + float shade = ((pos.z - max_heights.x) / (max_heights.z - max_heights.y)) * 5.0 + 0.3; return vec2(shade, density / (1.0 + vsum(abs(pos - cam_pos.xyz)) / 5000)); } @@ -40,9 +45,11 @@ vec2 cloud_at(vec3 pos) { vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, float quality) { const int ITERS = 12; const float INCR = 1.0 / ITERS; + origin = origin + focus_off.xyz; - float mind = (CLOUD_HEIGHT_MIN - origin.z) / dir.z; - float maxd = (CLOUD_HEIGHT_MAX - origin.z) / dir.z; + vec3 max_heights = get_cloud_heights(); + float mind = (max_heights.y - origin.z) / dir.z; + float maxd = (max_heights.z - origin.z) / dir.z; float start = max(min(mind, maxd), 0.0); float delta = min(abs(mind - maxd), max_dist); @@ -57,11 +64,11 @@ vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, f dist += fuzz * 0.01 * min(pow(dist * 0.005, 2.0), 1.0); vec3 pos = origin + dir * min(dist, max_dist); - vec2 sample = cloud_at(pos); + vec2 sample_ = cloud_at(pos); - float integral = sample.y * INCR; + float integral = sample_.y * INCR; passthrough *= 1.0 - integral; - cloud_shade = mix(cloud_shade, sample.x, passthrough * integral); + cloud_shade = mix(cloud_shade, sample_.x, passthrough * integral); dist += INCR * delta; if (passthrough < 0.1) { @@ -70,7 +77,7 @@ vec4 get_cloud_color(vec3 dir, vec3 origin, float time_of_day, float max_dist, f } } - float total_density = 1.0 - passthrough / (1.0 + delta * 0.0001); + float total_density = 1.0 - passthrough / (1.0 + pow(max_dist, 0.5) * 0.0001 + max((0.015 - dir.z) * 0.0001, 0.0) * max_dist); total_density = max(total_density - 1.0 / pow(max_dist, 0.25), 0.0); // Hack diff --git a/assets/voxygen/shaders/include/constants.glsl b/assets/voxygen/shaders/include/constants.glsl new file mode 100644 index 0000000000..d1f793af15 --- /dev/null +++ b/assets/voxygen/shaders/include/constants.glsl @@ -0,0 +1,73 @@ +/* NOTE: When included, this file will contain values for the automatically defined settings specified below. */ + +/* TODO: Add the ability to control the tendency to do stuff in the vertex vs. fragment shader. + * Currently this flag is ignored and always set to prefer fragment, but this tradeoff is not correct on all + * machines in all cases (mine, for instance). */ +#define VOXYGEN_COMPUTATION_PREERENCE_FRAGMENT 0 +#define VOXYGEN_COMPUTATION_PREERENCE_VERTEX 1 + +#define FLUID_MODE_CHEAP 0 +#define FLUID_MODE_SHINY 1 + +#define CLOUD_MODE_NONE 0 +#define CLOUD_MODE_REGULAR 1 + +#define LIGHTING_ALGORITHM_LAMBERTIAN 0 +#define LIGHTING_ALGORITHM_BLINN_PHONG 1 +#define LIGHTING_ALGORITHM_ASHIKHMIN 2 + +#define SHADOW_MODE_NONE 0 +#define SHADOW_MODE_CHEAP 1 +#define SHADOW_MODE_MAP 2 + +/* Unlike the other flags (for now anyway), these are bitmask values */ +#define LIGHTING_TYPE_REFLECTION 0x01 +#define LIGHTING_TYPE_TRANSMISSION 0x02 + +/* Currently ignored, but ideally shoud be helpful for determining light transport properties. */ +#define LIGHTING_REFLECTION_KIND_DIFFUSE 0 +#define LIGHTING_REFLECTION_KIND_GLOSSY 1 +#define LIGHTING_REFLECTION_KIND_SPECULAR 2 + +#define LIGHTING_TRANSPORT_MODE_IMPORTANCE 0 +/* Radiance mode is currently used as a proxy for "attenuation and medium materials + * matter," but we may make it more granular. */ +#define LIGHTING_TRANSPORT_MODE_RADIANCE 1 + +#define LIGHTING_DISTRIBUTION_SCHEME_MICROFACET 0 +#define LIGHTING_DISTRIBUTION_SCHEME_VOXEL 1 + +#define LIGHTING_DISTRIBUTION_BECKMANN 0 +#define LIGHTING_DISTRIBUTION_TROWBRIDGE 1 + +/* Constants expected to be defined automatically by configuration: */ + +/* +#define VOXYGEN_COMPUTATION_PREERENCE +#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 + * (but whose values may take automatically defined constants into account): */ + +/* +// At least one of LIGHTING_TYPE_REFLECTION or LIGHTING_TYPE_TRANSMISSION should be set. +#define LIGHTING_TYPE +#define LIGHTING_REFLECTION_KIND +#define LIGHTING_TRANSPORT_MODE +#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 +// When set, "full" LOD terrain informatino is available (e.g. terrain colors). +#define HAS_LOD_FULL_INFO +*/ diff --git a/assets/voxygen/shaders/include/globals.glsl b/assets/voxygen/shaders/include/globals.glsl index 895492a23a..b1ceefe8bd 100644 --- a/assets/voxygen/shaders/include/globals.glsl +++ b/assets/voxygen/shaders/include/globals.glsl @@ -4,12 +4,16 @@ 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; + vec4 shadow_proj_factors; uvec4 medium; ivec4 select_pos; vec4 gamma; diff --git a/assets/voxygen/shaders/include/light.glsl b/assets/voxygen/shaders/include/light.glsl index ee0920ccc6..295abc2c0b 100644 --- a/assets/voxygen/shaders/include/light.glsl +++ b/assets/voxygen/shaders/include/light.glsl @@ -1,11 +1,15 @@ +#include +#include + struct Light { vec4 light_pos; vec4 light_col; + // mat4 light_proj; }; layout (std140) uniform u_lights { - Light lights[32]; + Light lights[31]; }; struct Shadow { @@ -17,21 +21,45 @@ uniform u_shadows { Shadow shadows[24]; }; -#include - -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 attenuation_strength(vec3 rpos) { // This is not how light attenuation works at all, but it produces visually pleasing and mechanically useful properties float d2 = rpos.x * rpos.x + rpos.y * rpos.y + rpos.z * rpos.z; return max(2.0 / pow(d2 + 10, 0.35) - pow(d2 / 50000.0, 0.8), 0.0); } +// // 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). +// // +// // wpos is the position of the point being hit. +// // ray_dir is the reversed direction of the ray (going "out" of the point being hit). +// // surface_alt is the estimated altitude of the horizontal surface separating the substance from air. +// // defaultpos is the position to use in computing the distance along material at this point if there was a failure. +// // +// // Ideally, defaultpos is set so we can avoid branching on error. +// float compute_attenuation_beam(vec3 wpos, vec3 ray_dir, float surface_alt, vec3 defaultpos, float attenuation_depth) { +// vec3 water_intersection_surface_camera = vec3(cam_pos); +// bool _water_intersects_surface_camera = IntersectRayPlane(f_pos, view_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), cam_surface_dir, water_intersection_surface_camera); +// // Should work because we set it up so that if IntersectRayPlane returns false for camera, its default intersection point is cam_pos. +// float water_depth_to_camera = length(water_intersection_surface_camera - f_pos); +// +// vec3 water_intersection_surface_light = f_pos; +// bool _light_intersects_surface_water = IntersectRayPlane(f_pos, sun_dir.z <= 0.0 ? sun_dir : moon_dir, vec3(0.0, 0.0, /*f_alt*/f_pos.z + f_light), vec3(0.0, 0.0, 1.0), water_intersection_surface_light); +// // Should work because we set it up so that if IntersectRayPlane returns false for light, its default intersection point is f_pos-- +// // i.e. if a light ray can't hit the water, it shouldn't contribute to coloring at all. +// float water_depth_to_light = length(water_intersection_surface_light - f_pos); +// +// // For ambient color, we just take the distance to the surface out of laziness. +// float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0); +// +// // Color goes down with distance... +// // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law. +// vec3 water_color_direct = exp(-water_attenuation * (water_depth_to_light + water_depth_to_camera)); +// vec3 water_color_ambient = exp(-water_attenuation * (water_depth_to_vertical + water_depth_to_camera)); +// +// } + vec3 light_at(vec3 wpos, vec3 wnorm) { - const float LIGHT_AMBIENCE = 0.025; + const float LIGHT_AMBIANCE = 0.025; vec3 light = vec3(0); @@ -40,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; @@ -50,7 +78,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { // Multiply the vec3 only once vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a); - light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIENCE); + light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIANCE); } return light; } @@ -58,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; @@ -72,8 +103,130 @@ 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, 1.0); + // return min(shadow * shadow, 1.0); +#endif +} + +// Returns computed maximum intensity. +// +// 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, 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 directed_light = vec3(0.0); + vec3 max_light = vec3(0.0); + + const float LIGHT_AMBIANCE = 0.0;//0.015625; + + 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 - focus_off.xyz; + + // Pre-calculate difference between light and fragment + vec3 difference = light_pos - wpos; + float distance_2 = dot(difference, difference); + + // float strength = attenuation_strength(difference);// pow(attenuation_strength(difference), 0.6); + // // NOTE: This normalizes strength to 1.0 at the center of the point source. + // float strength = 1.0 / (1.0 + distance_2); + float strength = 1.0 / distance_2; + + // Multiply the vec3 only once + const float PI = 3.1415926535897932384626433832795; + const float PI_2 = 2 * PI; + float square_factor = /*2.0 * PI_2 * *//*2.0 * */L.light_col.a; + vec3 color = /*srgb_to_linear*/L.light_col.rgb; + + // // Only access the array once + // Shadow S = shadows[i]; + + // vec3 shadow_pos = S.shadow_pos_radius.xyz; + // float radius = S.shadow_pos_radius.w; + + // vec3 diff = shadow_pos - wpos; + // if (diff.z >= 0.0) { + // diff.z = -sign(diff.z) * diff.z * 0.1; + // } + + // 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*/0.0); + + // shadow = min(shadow, shade); + + // Compute reflectance. + float light_distance = sqrt(distance_2); + vec3 light_dir = -difference / light_distance; // normalize(-difference); + // light_dir = faceforward(light_dir, wnorm, light_dir); + bool is_direct = dot(-light_dir, wnorm) > 0.0; + // reflected_light += color * (distance_2 == 0.0 ? vec3(1.0) : light_reflection_factor(wnorm, cam_to_frag, light_dir, k_d, k_s, alpha)); + vec3 direct_light_dir = is_direct ? light_dir : -light_dir; + // vec3 direct_norm_dir = is_direct ? wnorm : -wnorm; + // Compute attenuation due to fluid. + // Default is light_pos, so we take the whole segment length for this beam if it never intersects the surface, unlesss the beam itself + // is above the surface, in which case we take zero (wpos). + color *= cam_attenuation * compute_attenuation_point(wpos, -direct_light_dir, mu, surface_alt, light_pos.z < surface_alt ? light_pos : wpos); + +#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_norm, voxel_lighting); + float computed_shadow = ShadowCalculationPoint(i, -difference, wnorm, wpos/*, light_distance*/); + // directed_light += is_direct ? max(computed_shadow, /*LIGHT_AMBIANCE*/0.0) * direct_light * square_factor : vec3(0.0); + directed_light += is_direct ? mix(LIGHT_AMBIANCE, 1.0, computed_shadow) * direct_light * square_factor : vec3(0.0); + // directed_light += (is_direct ? 1.0 : LIGHT_AMBIANCE) * max(computed_shadow, /*LIGHT_AMBIANCE*/0.0) * direct_light * square_factor;// : vec3(0.0); + // directed_light += mix(LIGHT_AMBIANCE, 1.0, computed_shadow) * direct_light * square_factor; + // ambient_light += is_direct ? vec3(0.0) : vec3(0.0); // direct_light * square_factor * LIGHT_AMBIANCE; + // ambient_light += is_direct ? direct_light * (1.0 - square_factor * LIGHT_AMBIANCE) : vec3(0.0); + + vec3 cam_light_diff = light_pos - focus_pos.xyz; + float cam_distance_2 = dot(cam_light_diff, cam_light_diff);// + 0.0001; + float cam_strength = 1.0 / (/*4.0 * *//*PI * *//*1.0 + */cam_distance_2); + + // vec3 cam_pos_diff = cam_to_frag.xyz - wpos; + // float pos_distance_2 = dot(cam_pos_diff, cam_pos_diff);// + 0.0001; + + // float cam_distance = sqrt(cam_distance_2); + // float distance = sqrt(distance_2); + float both_strength = cam_distance_2 == 0.0 ? distance_2 == 0.0 ? 0.0 : strength/* * strength*//*1.0*/ : distance_2 == 0.0 ? cam_strength/* * cam_strength*//*1.0*/ : + // 1.0 / (cam_distance * distance); + // sqrt(cam_strength * strength); + cam_strength + strength; + // (cam_strength * strength); + // max(cam_strength, strength); + // mix(cam_strength, strength, distance_2 / (cam_distance_2 + distance_2)); + // 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(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_AMBIANCE); + // Compute emiittance. + // float ambient_sides = clamp(mix(0.15, 0.0, abs(dot(wnorm, light_dir)) * 10000.0), 0.0, 0.15); + // float ambient_sides = 0.0;// max(dot(wnorm, light_dir) - 0.15, 0.15); + // // float ambient_sides = 0.0; + // ambient_light += color * (ambient_sides + LIGHT_AMBIANCE); + } + + // 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); + 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, wnorm, 1.0, emitted_light, reflected_light); } diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl new file mode 100644 index 0000000000..2cb34b406c --- /dev/null +++ b/assets/voxygen/shaders/include/lod.glsl @@ -0,0 +1,289 @@ +#include +#include +#include + +uniform sampler2D t_alt; +uniform sampler2D t_horizon; + +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 = (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 = (focus_off.xy + pos + 16) / 32.0; + return vec2(uv_pos.x, uv_pos.y); +} + +// textureBicubic from https://stackoverflow.com/a/42179924 +vec4 cubic(float v) { + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) * (1.0/6.0); +} + +// NOTE: We assume the sampled coordinates are already in "texture pixels". +vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { + vec2 texSize = textureSize(sampler, 0); + vec2 invTexSize = 1.0 / texSize; + /* texCoords.y = texSize.y - texCoords.y; */ + + texCoords = texCoords/* * texSize */ - 0.5; + + + vec2 fxy = fract(texCoords); + texCoords -= fxy; + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy; + // vec4 c = texCoords.xxyy + vec2 (-1, +1).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s; + + offset *= invTexSize.xxyy; + /* // Correct for map rotaton. + offset.zw = 1.0 - offset.zw; */ + + vec4 sample0 = texture(sampler, offset.xz); + vec4 sample1 = texture(sampler, offset.yz); + vec4 sample2 = texture(sampler, offset.xw); + vec4 sample3 = texture(sampler, offset.yw); + // vec4 sample0 = texelFetch(sampler, offset.xz, 0); + // vec4 sample1 = texelFetch(sampler, offset.yz, 0); + // vec4 sample2 = texelFetch(sampler, offset.xw, 0); + // vec4 sample3 = texelFetch(sampler, offset.yw, 0); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix( + mix(sample3, sample2, sx), mix(sample1, sample0, sx) + , sy); +} + +float alt_at(vec2 pos) { + return (/*round*/(texture/*textureBicubic*/(t_alt, pos_to_uv(t_alt, pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); + //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; + + // return 0.0 + // + pow(texture(t_noise, pos * 0.00005).x * 1.4, 3.0) * 1000.0 + // + texture(t_noise, pos * 0.001).x * 100.0 + // + texture(t_noise, pos * 0.003).x * 30.0; +} + +float alt_at_real(vec2 pos) { + // Basic idea: only really need the real altitude for an accurate water height estimation, so if we are in the cheap shader take a shortcut. +// #if (FLUID_MODE == FLUID_MODE_CHEAP) +// return alt_at(pos); +// #elif (FLUID_MODE == FLUID_MODE_SHINY) + return (/*round*/(textureBicubic(t_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); +// #endif + //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; + + // return 0.0 + // + pow(texture(t_noise, pos * 0.00005).x * 1.4, 3.0) * 1000.0 + // + texture(t_noise, pos * 0.001).x * 100.0 + // + texture(t_noise, pos * 0.003).x * 30.0; +} + + +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*/; + + // return 1.0; +/* + + let shade_frac = horizon_map + .and_then(|(angles, heights)| { + chunk_idx + .and_then(|chunk_idx| angles.get(chunk_idx)) + .map(|&e| (e as f64, heights)) + }) + .and_then(|(e, heights)| { + chunk_idx + .and_then(|chunk_idx| heights.get(chunk_idx)) + .map(|&f| (e, f as f64)) + }) + .map(|(angle, height)| { + let w = 0.1; + if angle != 0.0 && light_direction.x != 0.0 { + let deltax = height / angle; + let lighty = (light_direction.y / light_direction.x * deltax).abs(); + let deltay = lighty - height; + let s = (deltay / deltax / w).min(1.0).max(0.0); + // Smoothstep + s * s * (3.0 - 2.0 * s) + } else { + 1.0 + } + }) + .unwrap_or(1.0); +*/ + // vec2 f_horizon; + /* if (light_dir.z >= 0) { + return 0.0; + } */ + /* if (light_dir.x >= 0) { + f_horizon = f_horizons.rg; + // f_horizon = f_horizons.ba; + } else { + f_horizon = f_horizons.ba; + // f_horizon = f_horizons.rg; + } + return 1.0; */ + /* bvec2 f_mode = lessThan(vec2(light_dir.x), vec2(1.0)); + f_horizon = mix(f_horizons.ba, f_horizons.rg, f_mode); */ + // f_horizon = mix(f_horizons.rg, f_horizons.ba, clamp(light_dir.x * 10000.0, 0.0, 1.0)); + vec2 f_horizon = mix(f_horizons.rg, f_horizons.ba, bvec2(light_dir.x < 0.0)); + // vec2 f_horizon = mix(f_horizons.ba, f_horizons.rg, clamp(light_dir.x * 10000.0, 0.0, 1.0)); + // f_horizon = mix(f_horizons.ba, f_horizons.rg, bvec2(lessThan(light_dir.xx, vec2(0.0)))); + /* if (f_horizon.x <= 0) { + return 1.0; + } */ + float angle = tan(f_horizon.x * PI_2); + /* if (angle <= 0.0001) { + return 1.0; + } */ + float height = f_horizon.y * /*1300.0*//*1278.7266845703125*/view_distance.w + view_distance.z; + const float w = 0.1; + float deltah = height - alt - focus_off.z; + //if (deltah < 0.0001/* || angle < 0.0001 || abs(light_dir.x) < 0.0001*/) { + // return 1.0; + /*} else */{ + float lighta = /*max*/(-light_dir.z/*, 0.0*/) / max(abs(light_dir.x), 0.0001); + // NOTE: Ideally, deltah <= 0.0 is a sign we have an oblique horizon angle. + float deltax = deltah / max(angle, 0.0001)/*angle*/; + float lighty = lighta * deltax; + float deltay = lighty - deltah + max(pos.z - alt, 0.0); + // NOTE: the "real" deltah should always be >= 0, so we know we're only handling the 0 case with max. + float s = mix(max(min(max(deltay, 0.0) / max(deltax, 0.0001) / w, 1.0), 0.0), 1.0, deltah <= 0); + return max(/*0.2 + 0.8 * */(s * s * (3.0 - 2.0 * s)), MIN_LIGHT); + /* if (lighta >= angle) { + return 1.0; + } else { + return MIN_LIGHT; + } */ + // float deltah = height - alt; + // float deltah = max(height - alt, 0.0); + // float lighty = abs(sun_dir.z / sun_dir.x * deltax); + // float lighty = abs(sun_dir.z / sun_dir.x * deltax); + // float deltay = lighty - /*pos.z*//*deltah*/(deltah + max(pos.z - alt, 0.0))/*deltah*/; + // float s = max(min(max(deltay, 0.0) / deltax / w, 1.0), 0.0); + // Smoothstep + // return max(/*0.2 + 0.8 * */(s * s * (3.0 - 2.0 * s)), MIN_LIGHT); + } +} + +// 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; + float len_2 = dot(pos, pos); + float len_pow = len_2 * sqrt(len_2); + // float len_pow = pow(len/* * SQRT_2*//* * 0.5*/, 3.0); + // vec2 splayed = pos * pow(len * 0.5, 3.0) * SPLAY_MULT; + const float SQRT_2 = sqrt(2.0) / 2.0; + // /const float CBRT_2 = cbrt(2.0) / 2.0; + // vec2 splayed = pos * (view_distance.x * SQRT_2 + pow(len * 0.5, 3.0) * (SPLAY_MULT - view_distance.x)); + vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(t_alt, 0) * 32.0/* - view_distance.x*/)); + return splayed; + + // Radial: pos.x = r - view_distance.x from focus_pos, pos.y = θ from cam_pos to focus_pos on xy plane. + // const float PI_2 = 3.1415926535897932384626433832795; + // float squared = pos.x * pos.x; + // // // vec2 splayed2 = pos * vec2(squared * (SPLAY_MULT - view_distance.x), PI); + // vec2 splayed2 = pos * vec2(squared * (textureSize(t_alt, 0).x * 32.0 - view_distance.x), PI); + // float r = splayed2.x + view_distance.x; + // vec2 theta = vec2(cos(splayed2.y), sin(splayed2.y)); + // return r * theta; + // // mat2 rot_mat = mat2(vec2(theta.x, -theta.y), theta.yx); + // // return r * /*normalize(normalize(focus_pos.xy - cam_pos.xy) + theta);*/rot_mat * normalize(focus_pos.xy - cam_pos.xy); + // return splayed; +} + +vec3 lod_norm(vec2 f_pos/*vec3 pos*/, vec4 square) { + // const float SAMPLE_W = 32; + + // vec2 f_pos = pos.xy; + // float altx0 = alt_at_real(f_pos + vec2(-1.0, 0) * SAMPLE_W); + // float altx1 = alt_at_real(f_pos + vec2(1.0, 0) * SAMPLE_W); + // float alty0 = alt_at_real(f_pos + vec2(0, -1.0) * SAMPLE_W); + // float alty1 = alt_at_real(f_pos + vec2(0, 1.0) * SAMPLE_W); + float altx0 = alt_at(vec2(square.x, f_pos.y)); + float altx1 = alt_at(vec2(square.z, f_pos.y)); + float alty0 = alt_at(vec2(f_pos.x, square.y)); + float alty1 = alt_at(vec2(f_pos.x, square.w)); + float slope = abs(altx1 - altx0) + abs(alty0 - alty1); + + // vec3 norm = normalize(cross( + // vec3(/*2.0 * SAMPLE_W*/square.z - square.x, 0.0, altx1 - altx0), + // 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), + 1.0 + //(abs(square.w - square.y) + abs(square.z - square.x)) / (slope + 0.00001) // Avoid NaN + )); + /* vec3 norm = normalize(vec3( + (altx0 - altx1) / (2.0 * SAMPLE_W), + (alty0 - alty1) / (2.0 * SAMPLE_W), + (2.0 * SAMPLE_W) / (slope + 0.00001) // Avoid NaN + )); */ + + return faceforward(norm, vec3(0.0, 0.0, -1.0)/*pos - cam_pos.xyz*/, norm); +} + +vec3 lod_norm(vec2 f_pos/*vec3 pos*/) { + const float SAMPLE_W = 32; + + return lod_norm(f_pos, vec4(f_pos - vec2(SAMPLE_W), f_pos + vec2(SAMPLE_W))); +} + + +vec3 lod_pos(vec2 pos, vec2 focus_pos) { + // Remove spiking by "pushing" vertices towards local optima + vec2 delta = splay(pos); + vec2 hpos = focus_pos + delta; + vec2 nhpos = hpos; + // vec2 lod_shift = splay(abs(pos) - 1.0 / view_distance.y); + float shift = 15.0;// min(lod_shift.x, lod_shift.y) * 0.5; + for (int i = 0; i < 3; i ++) { + // vec4 square = focus_pos.xy + vec4(splay(pos - vec2(1.0, 1.0), splay(pos + vec2(1.0, 1.0)))); + nhpos -= lod_norm(hpos).xy * shift; + } + hpos = hpos + normalize(nhpos - hpos + 0.001) * min(length(nhpos - hpos), 32); + + return vec3(hpos, alt_at_real(hpos)); +} + +#ifdef HAS_LOD_FULL_INFO +uniform sampler2D t_map; + +vec3 lod_col(vec2 pos) { + //return vec3(0, 0.5, 0); + // return /*linear_to_srgb*/vec3(alt_at(pos), textureBicubic(t_map, pos_to_tex(pos)).gb); + return /*linear_to_srgb*/(textureBicubic(t_map, pos_to_tex(pos)).rgb) + ;//+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; + //+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; +} +#endif diff --git a/assets/voxygen/shaders/include/random.glsl b/assets/voxygen/shaders/include/random.glsl index 7d217d858e..7f297866f2 100644 --- a/assets/voxygen/shaders/include/random.glsl +++ b/assets/voxygen/shaders/include/random.glsl @@ -1,3 +1,5 @@ +uniform sampler2D t_noise; + float hash(vec4 p) { p = fract(p * 0.3183099 + 0.1) - fract(p + 23.22121); p *= 17.0; diff --git a/assets/voxygen/shaders/include/shadows.glsl b/assets/voxygen/shaders/include/shadows.glsl new file mode 100644 index 0000000000..e5968a0a2a --- /dev/null +++ b/assets/voxygen/shaders/include/shadows.glsl @@ -0,0 +1,218 @@ +#ifdef HAS_SHADOW_MAPS + + #if (SHADOW_MODE == SHADOW_MODE_MAP) +struct ShadowLocals { + mat4 shadowMatrices; + mat4 texture_mat; +}; + +layout (std140) +uniform u_light_shadows { + ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +}; + +uniform sampler2DShadow t_directed_shadow_maps; +// uniform sampler2DArrayShadow t_directed_shadow_maps; + +// uniform samplerCubeArrayShadow t_shadow_maps; +// uniform samplerCubeArray t_shadow_maps; +uniform samplerCubeShadow t_point_shadow_maps; +// uniform samplerCube t_shadow_maps; + +// uniform sampler2DArray t_directed_shadow_maps; + +float VectorToDepth (vec3 Vec) +{ + // return length(Vec) / screen_res.w; + 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 ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos) +{ + if (lightIndex != 0u) { + return 1.0; + }; + + { + float currentDepth = VectorToDepth(fragToLight);// + bias; + + float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); + /* if (visibility == 1.0 || visibility == 0.0) { + return visibility; + } */ + /* if (visibility >= 0.75) { + return 1.0; + } + if (visibility <= 0.25) { + return 0.0; + } */ + /* if (visibility < 1.0) { + return 0.0; + } */ + // return visibility; + /* if (visibility == 1.0) { + return visibility; + } */ + return visibility; + // return visibility == 1.0 ? 1.0 : 0.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 viewDistance = length(cam_pos.xyz - fragPos); + // vec3 firstDelta = vec3(0.0);///*min(viewDistance, 5.0) * *//**normalize(cam_pos - fragPos)*/fragNorm * 0.5; + // fragToLight += firstDelta; + // // viewDistance -= length(firstDelta); + // fragPos -= firstDelta; + + // int samples = 20; + // // float lightDistance = length(fragToLight); + // // float diskRadius = 0.00001; + // // float diskRadius = 1.0; + // // float diskRadius = 0.05; + // float diskRadius = 5.0 / screen_res.w;// (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_point_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; +} + +float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_pos, vec3 fragPos) +{ + float bias = 0.000;//0.0005;//-0.0001;// 0.05 / (2.0 * view_distance.x); + float diskRadius = 0.01; + 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; + // sun_pos.z += sun_pos.w * bias; + ShadowLocals sun_shadow = shadowMats[0]; + vec4 sun_pos = sun_shadow.texture_mat * vec4(fragPos, 1.0); + // sun_pos.z -= sun_pos.w * bias; + float visibility = textureProj(t_directed_shadow_maps, sun_pos); + /* float visibilityLeft = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, -diskRadius, 0.0), 1.0)); + float visibilityRight = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, diskRadius, 0.0), 1.0)); */ + // float nearVisibility = textureProj(t_directed_shadow_maps + vec3(0.001, sun_pos)); + // float visibility = textureProj(t_directed_shadow_maps, vec4(fragPos.xy, /*lightIndex, */fragPos.z + bias, sun_pos.w)); + // return visibility; + // return min(visibility, min(visibilityLeft, visibilityRight)); + // 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 - 0.5) * (visibility - 0.5) * 2.0 * sign(visibility - 0.5) + 0.5;// visibility > 0.75 ? visibility : 0.0;// visibility > 0.9 ? 1.0 : 0.0; + return visibility; + // return visibility == 1.0 ? 1.0 : 0.0; + // return abs(fragPos.y - round(fragPos.y)) <= 0.1 || abs(fragPos.x - round(fragPos.x)) <= 0.1 ? ( visibility == 1.0 ? 1.0 : 0.0) : visibility; + /* if (visibility == 1.0) { + return 1.0; + } */ + // return visibility; + /* if (fragPos.z > 1.0) { + return 1.0; + } */ + // vec3 snapToZ = abs(fragPos - vec3(ivec3(fragPos))); // fract(abs(fragPos)); + // // snapToZ = min(snapToZ, 1.0 - snapToZ); + // const float EDGE_DIST = 0.01; + // snapToZ = mix(vec3(0.0), vec3(1.0), lessThanEqual(snapToZ, vec3(EDGE_DIST))); + // // float snapToZDist = dot(snapToZ, snapToZ); + // if (visibility <= 0.75 && /*fract(abs(fragPos.xy)), vec2(0.1)))*/ /*snapToZDist <= 0.25*//*all(lessThan(snapToZ, vec3(0.1)))(*/ + // snapToZ.x + snapToZ.y + snapToZ.z >= 2.0) { + // 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.0003;//0.005;// / (2.0 * view_distance.x);//(1.0 + (viewDistance / screen_res.w)) / 25.0; + // fragPos = sun_pos.xyz / sun_pos.w; + // for(int i = 0; i < samples; ++i) + // { + // vec3 currentDepth = fragPos + vec3(sampleOffsetDirections[i].xyz) * diskRadius + bias; + // visibility = texture(t_directed_shadow_maps, currentDepth);//vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/); + // // visibility = texture(t_directed_shadow_maps, vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/); + // shadow += visibility; + // // mix(visibility, 1.0, visibility >= 0.5); + // } + // shadow /= float(samples); + // return shadow; +} + #elif (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_CHEAP) +float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos) +{ + return 1.0; +} + #endif +#else +float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos) +{ + return 1.0; +} +#endif diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index fe12499086..07bbdd67a4 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -1,215 +1,589 @@ #include +#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; const vec3 SKY_DAY_TOP = vec3(0.1, 0.5, 0.9); const vec3 SKY_DAY_MID = vec3(0.02, 0.28, 0.8); const vec3 SKY_DAY_BOT = vec3(0.1, 0.2, 0.3); -const vec3 DAY_LIGHT = vec3(1.2, 1.0, 1.0); +const vec3 DAY_LIGHT = vec3(1.9, 1.75, 0.9);//vec3(1.5, 1.4, 1.0); const vec3 SUN_HALO_DAY = vec3(0.35, 0.35, 0.0); const vec3 SKY_DUSK_TOP = vec3(0.06, 0.1, 0.20); const vec3 SKY_DUSK_MID = vec3(0.35, 0.1, 0.15); const vec3 SKY_DUSK_BOT = vec3(0.0, 0.1, 0.23); -const vec3 DUSK_LIGHT = vec3(3.0, 1.5, 0.3); +const vec3 DUSK_LIGHT = vec3(9.0, 1.5, 0.15); const vec3 SUN_HALO_DUSK = vec3(1.2, 0.15, 0.0); const vec3 SKY_NIGHT_TOP = vec3(0.001, 0.001, 0.0025); const vec3 SKY_NIGHT_MID = vec3(0.001, 0.005, 0.02); const vec3 SKY_NIGHT_BOT = vec3(0.002, 0.004, 0.004); -const vec3 NIGHT_LIGHT = vec3(0.002, 0.01, 0.03); +const vec3 NIGHT_LIGHT = vec3(0.002, 0.02, 0.02); +// const vec3 NIGHT_LIGHT = vec3(0.0, 0.0, 0.0); + +// Linear RGB, scattering coefficients for atmosphere at roughly R, G, B wavelengths. +// +// See https://en.wikipedia.org/wiki/Diffuse_sky_radiation +const vec3 MU_SCATTER = vec3(0.05, 0.10, 0.23) * 1.5; + +const float SUN_COLOR_FACTOR = 5.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); +const float PERSISTENT_AMBIANCE = 1.0 / 32.0;// 1.0 / 80; // 1.0 / 512; // 0.00125 // 0.1;// 0.025; // 0.1; - float sun_angle_rad = time_of_day * TIME_FACTOR; - return vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad)); +//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; } -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; - return normalize(-vec3(sin(moon_angle_rad), 0.0, cos(moon_angle_rad) - 0.5)); +float get_moon_brightness(/*vec3 moon_dir*/) { + return max(-moon_dir.z + 0.6, 0.0) * 0.4; } -const float PERSISTENT_AMBIANCE = 0.1; - -float get_sun_brightness(vec3 sun_dir) { - return max(-sun_dir.z + 0.6, 0.0) * 0.9; +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) + ); } -float get_moon_brightness(vec3 moon_dir) { - return max(-moon_dir.z + 0.6, 0.0) * 0.07; +vec3 get_moon_color(/*vec3 moon_dir*/) { + return vec3(0.05, 0.05, 0.6); } -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) - ); +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, */shadow/*, get_sun_color(dir), get_sun_brightness(dir)*/); } -vec3 get_moon_color(vec3 moon_dir) { - return vec3(0.05, 0.05, 0.6); +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)*/); } -void get_sun_diffuse(vec3 norm, float time_of_day, out vec3 light, out vec3 diffuse_light, out vec3 ambient_light, float diffusion) { - const float SUN_AMBIANCE = 0.1; +// // Calculates extra emission and reflectance (due to sunlight / moonlight). +// // +// // reflectence = k_a * i_a + i_a,persistent +// // emittence = Σ { m ∈ lights } i_m * shadow_m * get_light_reflected(light_m) +// // +// // Note that any shadowing to be done that would block the sun and moon, aside from heightmap shadowing (that will be +// // implemented sooon), should be implicitly provided via k_a, k_d, and k_s. For instance, shadowing via ambient occlusion. +// // +// // Also note that the emitted light calculation is kind of lame... we probabbly need something a bit nicer if we ever want to do +// // 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; +// +// 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); +// +// 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; +// +// /* float NLsun = max(dot(-norm, sun_dir), 0); +// float NLmoon = max(dot(-norm, moon_dir), 0); +// vec3 E = -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(-norm, sun_dir)) * mix(0.0, 1.0, abs(sun_dir.z) * 10000.0) * 10000.0), 0.0, 0.5); +// // float ambient_sides = 0.5 - 0.5 * abs(dot(-norm, sun_dir)); +// +// emitted_light = k_a * (ambient_sides + vec3(SUN_AMBIANCE * sun_light + moon_light)) + PERSISTENT_AMBIANCE; +// // TODO: Add shadows. +// reflected_light = +// 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); */ +// } - vec3 sun_dir = get_sun_dir(time_of_day); - vec3 moon_dir = get_moon_dir(time_of_day); +// Returns computed maximum intensity. +// +// wpos is the position of this fragment. +// 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(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); - 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_chroma = sun_color * sun_light; - vec3 moon_chroma = moon_color * moon_light; + 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; - 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); + // 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; + +// #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 + // + // HdRd radiation should come in at angle normal to us. + // const float H_d = 0.23; + // + // Let β be the angle from horizontal + // (for objects exposed to the sky, where positive when sloping towards south and negative when sloping towards north): + // + // sin β = (north ⋅ norm) / |north||norm| + // = dot(vec3(0, 1, 0), norm) + // + // cos β = sqrt(1.0 - dot(vec3(0, 1, 0), norm)) + // + // Let h be the hour angle (180/0.0 at midnight, 90/1.0 at dawn, 0/0.0 at noon, -90/-1.0 at dusk, -180 at midnight/0.0): + // cos h = (midnight ⋅ -light_dir) / |midnight||-light_dir| + // = (noon ⋅ light_dir) / |noon||light_dir| + // = dot(vec3(0, 0, 1), light_dir) + // + // Let φ be the latitude at this point. 0 at equator, -90 at south pole / 90 at north pole. + // + // Let δ be the solar declination (angular distance of the sun's rays north [or south[] + // of the equator), i.e. the angle made by the line joining the centers of the sun and Earth with its projection on the + // equatorial plane. Caused by axial tilt, and 0 at equinoxes. Normally varies between -23.45 and 23.45 degrees. + // + // Let α (the solar altitude / altitud3 angle) be the vertical angle between the projection of the sun's rays on the + // horizontal plane and the direction of the sun's rays (passing through a point). + // + // Let Θ_z be the vertical angle between sun's rays and a line perpendicular to the horizontal plane through a point, + // i.e. + // + // Θ_z = (π/2) - α + // + // i.e. cos Θ_z = sin α and + // cos α = sin Θ_z + // + // Let γ_s be the horizontal angle measured from north to the horizontal projection of the sun's rays (positive when + // measured westwise). + // + // cos Θ_z = cos φ cos h cos δ + sin φ sin δ + // cos γ_s = sec α (cos φ sin δ - cos δ sin φ cos h) + // = (1 / √(1 - cos² Θ_z)) (cos φ sin δ - cos δ sin φ cos h) + // sin γ_s = sec α cos δ sin h + // = (1 / cos α) cos δ sin h + // = (1 / sin Θ_z) cos δ sin h + // = (1 / √(1 - cos² Θ_z)) cos δ sin h + // + // R_b = (sin(δ)sin(φ - β) + cos(δ)cos(h)cos(φ - β))/(sin(δ)sin(φ) + cos(δ)cos(h)cos(φ)) + // + // Assuming we are on the equator (i.e. φ = 0), and there is no axial tilt or we are at an equinox (i.e. δ = 0): + // + // cos Θ_z = 1 * cos h * 1 + 0 * 0 = cos h + // cos γ_s = (1 / √(1 - cos² h)) (1 * 0 - 1 * 0 * cos h) + // = (1 / √(1 - cos² h)) * 0 + // = 0 + // sin γ_s = (1 / √(1 - cos² h)) * sin h + // = sin h / sin h + // = 1 + // + // R_b = (0 * sin(0 - β) + 1 * cos(h) * cos(0 - β))/(0 * 0 + 1 * cos(h) * 1) + // = (cos(h)cos(-β)) / cos(H) + // = cos(-β), the angle from horizontal. + // + // NOTE: cos(-β) = cos(β). + // float cos_sun = dot(norm, /*-sun_dir*/vec3(0, 0, 1)); + // float cos_moon = dot(norm, -moon_dir); + // + // Let ζ = diffuse reflectance of surrounding ground for solar radiation, then we have + // + // R_d = (1 + cos β) / 2 + // R_r = ζ (1 - cos β) / 2 + // + // H_t = H_b R_b + H_d R_d + (H_b + H_d) R_r + float sin_beta = dot(vec3(0, 1, 0), norm); + float R_b = sqrt(1.0 - sin_beta * sin_beta); + // Rough estimate of diffuse reflectance of rest of ground. + // NOTE: zeta should be close to 0.7 with snow cover, 0.2 normally? Maybe? + vec3 zeta = max(vec3(0.2), k_d * (1.0 - k_s));//vec3(0.2);// k_d * (1.0 - k_s); + float R_d = (1 + R_b) * 0.5; + vec3 R_r = zeta * (1.0 - R_b) * 0.5; + // + // We can break this down into: + // H_t_b = H_b * (R_b + R_r) = light_intensity * (R_b + R_r) + // H_t_r = H_d * (R_d + R_r) = light_intensity * (R_d + R_r) + vec3 R_t_b = R_b + R_r; + 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*/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); + /* float NLsun = max(dot(-norm, sun_dir), 0); + float NLmoon = max(dot(-norm, moon_dir), 0); + vec3 E = -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 = 0.0; + // float ambient_sides = 0.5 - 0.5 * min(abs(dot(-norm, sun_dir)), abs(dot(-norm, moon_dir))); + // 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 = light_frac + k_a * PERSISTENT_AMBIANCE * MU_SCATTER; + // emitted_light = k_a * light_frac * (/*ambient_sides + */SUN_AMBIANCE * /*sun_light*/sun_chroma + /*vec3(moon_light)*/MOON_AMBIANCE * moon_chroma) + PERSISTENT_AMBIANCE; + + reflected_light = R_t_r * ( + (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 * 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); */ + return rel_luminance(emitted_light + reflected_light);//rel_luminance(emitted_light + reflected_light);//sun_chroma + moon_chroma + PERSISTENT_AMBIANCE; +} + +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(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) * (1.0 + hash(pos.yxzz) * 0.85); + // Noisy offsets + pos += (3.0 / star_scale) * (1.0 + hash(pos.yxzz) * 0.85); - // Find distance to fragment - float dist = length(pos - dir); + // Find distance to fragment + float dist = length(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) { +#if (CLOUD_MODE == CLOUD_MODE_NONE) + const bool has_clouds = false; +#elif (CLOUD_MODE == CLOUD_MODE_REGULAR) + const bool has_clouds = true; +#endif + + 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; + + // 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; + + // 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 + const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0; + + vec3 sun_halo_color = mix( + SUN_HALO_DUSK, + SUN_HALO_DAY, + max(-sun_dir.z, 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); + + // 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 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))); + + // 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_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_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_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_color = mix( + mix( + sky_mid, + sky_bot, + pow(max(-dir.z, 0), 0.4) + ), + sky_top, + max(dir.z, 0) + ); + + // Approximate distance to fragment + float f_dist = distance(origin, f_pos); + + // 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 + + 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) { - // Sky color - vec3 sun_dir = get_sun_dir(time_of_day); - vec3 moon_dir = get_moon_dir(time_of_day); - - // 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 - const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 200.0; - - vec3 sun_halo_color = mix( - SUN_HALO_DUSK, - SUN_HALO_DAY, - max(-sun_dir.z, 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; - vec3 sun_light = (sun_halo + sun_surf) * clamp(dir.z * 10.0, 0, 1); - - // 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 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))); - - // 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_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_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_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_color = mix( - mix( - sky_mid, - sky_bot, - pow(max(-dir.z, 0), 0.4) - ), - sky_top, - max(dir.z, 0) - ); - - // Approximate distance to fragment - float f_dist = distance(origin, f_pos); - - // 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); - - if (f_dist > 5000.0) { - sky_color += sun_light + moon_light; - } - - return mix(sky_color, clouds.rgb, clouds.a); + return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_stars, 1.0, clouds); } float fog(vec3 f_pos, vec3 focus_pos, uint medium) { - float fog_radius = view_distance.x; - float mist_radius = 10000000.0; + return max(1.0 - 5000.0 / (1.0 + distance(f_pos.xy, focus_pos.xy)), 0.0); - float min_fog = 0.5; - float max_fog = 1.0; + float fog_radius = view_distance.x; + float mist_radius = 10000000.0; - if (medium == 1u) { - mist_radius = UNDERWATER_MIST_DIST; - min_fog = 0.0; - } + float min_fog = 0.5; + float max_fog = 1.0; - float fog = distance(f_pos.xy, focus_pos.xy) / fog_radius; - float mist = distance(f_pos, focus_pos) / mist_radius; + if (medium == 1u) { + mist_radius = UNDERWATER_MIST_DIST; + min_fog = 0.0; + } - return pow(clamp((max(fog, mist) - min_fog) / (max_fog - min_fog), 0.0, 1.0), 1.7); + 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); +} + +/* 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); +} */ +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; + +#if (LIGHTING_ALGORITHM == LIGHTING_ALGORITHM_ASHIKHMIN) + const float DAY_SATURATION = 1.1; +#else + const float DAY_SATURATION = 1.0; +#endif + const float DUSK_SATURATION = 0.6; + const float NIGHT_SATURATION = 0.1; + + const float gamma = /*0.5*//*1.*0*/1.0;//1.0; + /* float light = length(emitted + reflected); + float color = srgb_to_linear(emitted + reflected); + float avg_col = (color.r + color.g + color.b) / 3.0; + return ((color - avg_col) * light + reflected * avg_col) * (emitted + reflected); */ + // float max_intensity = vec3(1.0); + vec3 color = emitted + reflected; + 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); */ + 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*/)); + + // 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.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); + float alph = sky_light > 0.0 && max_light > 0.0 ? mix(1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light / (0.0 + sky_light)), 1.0, clamp(max_light - sky_light, 0.0, 1.0)) : 1.0; + alpha = alpha * min(alph, 1.0);//((max_light > 0.0 && max_light > sky_light /* && sky_light > 0.0*/) ? /*1.0*/1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light - (0.0 + sky_light)) : 1.0); + // alpha = alpha * min(1.0, (max_light == 0.0 ? 1.0 : (1.0 + abs(lum_sky)) / /*(1.0 + max_light)*/max_light)); + + vec3 col_adjusted = lum == 0.0 ? vec3(0.0) : color / lum; + + // float L = lum == 0.0 ? 0.0 : log(lum); + + + // // float B = T; + // // float B = L + log(alpha); + // float B = lum; + + // float D = L - B; + + // float o = 0.0;//log(PERSISTENT_AMBIANCE); + // float scale = /*-alpha*/-alpha;//1.0; + + // float B_ = (B - o) * scale; + + // // float T = lum; + // float O = exp(B_ + D); + + float T = 1.0 - exp(-alpha * lum);//lum / (1.0 + lum); + // float T = lum; + + // 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) + ); + // s = max(s, (max_light) / (1.0 + s)); + s = max(s, max_light / (1.0 + max_light)); + + vec3 c = pow(col_adjusted, vec3(s)) * T; + // vec3 c = col_adjusted * T; + // vec3 c = sqrt(col_adjusted) * T; + // vec3 c = /*col_adjusted * */col_adjusted * T; + + // return color; + return c; + // float sum_col = color.r + color.g + color.b; + // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); } diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index a56a97b384..9db36c1cb1 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -1,3 +1,24 @@ +// Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. +// See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water +const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); + +// // 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)); @@ -7,10 +28,592 @@ vec3 srgb_to_linear(vec3 srgb) { return mix(higher, lower, cutoff); } -vec3 linear_to_srgb(vec3 linear) { - bvec3 cutoff = lessThan(linear, vec3(0.0031308)); - vec3 higher = vec3(1.055) * pow(linear, vec3(1.0 / 2.4)) - vec3(0.055); - vec3 lower = linear * vec3(12.92); - - return mix(higher, lower, cutoff); +vec3 linear_to_srgb(vec3 col) { + // bvec3 cutoff = lessThan(col, vec3(0.0060)); + // return mix(11.500726 * col, , cutoff); + vec3 s1 = vec3(sqrt(col.r), sqrt(col.g), sqrt(col.b)); + vec3 s2 = vec3(sqrt(s1.r), sqrt(s1.g), sqrt(s1.b)); + vec3 s3 = vec3(sqrt(s2.r), sqrt(s2.g), sqrt(s2.b)); + return vec3( + mix(11.500726 * col.r, (0.585122381 * s1.r + 0.783140355 * s2.r - 0.368262736 * s3.r), clamp((col.r - 0.0060) * 10000.0, 0.0, 1.0)), + mix(11.500726 * col.g, (0.585122381 * s1.g + 0.783140355 * s2.g - 0.368262736 * s3.g), clamp((col.g - 0.0060) * 10000.0, 0.0, 1.0)), + mix(11.500726 * col.b, (0.585122381 * s1.b + 0.783140355 * s2.b - 0.368262736 * s3.b), clamp((col.b - 0.0060) * 10000.0, 0.0, 1.0)) + ); } + +float pow5(float x) { + float x2 = x * x; + return x2 * x2 * x; +} + +vec4 pow5(vec4 x) { + vec4 x2 = x * x; + return x2 * x2 * x; +} + +// Fresnel angle for perfectly specular dialectric materials. + +// Schlick approximation +vec3 schlick_fresnel(vec3 Rs, float cosTheta) { + // auto pow5 = [](Float v) { return (v * v) * (v * v) * v; }; + // return Rs + pow5(1 - cosTheta) * (Spectrum(1.) - Rs); + return Rs + pow5(1.0 - cosTheta) * (1.0 - Rs); +} + +// Beckmann Distribution +float BeckmannDistribution_D(float NdotH, float alpha) { + const float PI = 3.1415926535897932384626433832795; + float NdotH2 = NdotH * NdotH; + float NdotH2m2 = NdotH2 * alpha * alpha; + float k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + return mix(k_spec, 0.0, NdotH == 0.0); +} + +// Voxel Distribution +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); + + vec3 NdotH = wh * sides;//max(wh * sides, 0.0);/*cos_sides_i*///max(sides * wh, 0.0); + + const float PI = 3.1415926535897932384626433832795; + 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(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)); + + // float voxel_norm = 0.0; + // for (int i = 0; i < 6; i ++) { + // // Light reflecting off the half-angle can shine on up to three sides. + // // So, the idea here is to figure out the ratio of visibility of each of these + // // three sides such that their sum adds to 1, then computing a Beckmann Distribution for each side times + // // the this ratio. + // // + // // The ratio of these normals in each direction should be the sum of their cosines with the light over π, + // // I think. + // // + // // cos (wh, theta) + // // + // // - one normal + // // + // // The ratio of each of the three exposed sides should just be the slope. + // vec3 side = normals[i]; + // float side_share = max(dot(norm, side), 0.0); + // float NdotH = max(dot(wh, side), 0.0); + // voxel_norm += side_share * BeckmannDistribution_D(NdotH, alpha); + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // } + + // /* float NdotH = dot(wh, norm); + // float NdotH2 = NdotH * NdotH; + // float NdotH2m2 = NdotH2 * alpha * alpha; + + // float k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + // return mix(k_spec, 0.0, NdotH == 0.0); */ + // return voxel_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); + + vec3 NdotH = wh * sides;//max(wh * sides, 0.0);/*cos_sides_i*///max(sides * wh, 0.0); + + const float PI = 3.1415926535897932384626433832795; + vec3 NdotH2 = NdotH * NdotH; + // vec3 m2 = alpha * alpha; + // vec3 NdotH2m2 = NdotH2 * m2; + vec3 NdotH2m2 = NdotH2 * alpha * alpha; + // vec3 Tan2Theta = (1 - NdotH2) / NdotH2; + // vec3 e = (NdotH2 / m2 + (1 - NdotH2) / m2) * Tan2Theta; + // vec3 e = 1 / m2 * (1 - NdotH2) / NdotH2; + 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(voxel_norm)); +} + +float BeckmannDistribution_Lambda(vec3 norm, vec3 dir, float alpha) { + float CosTheta = /*max(dot(norm, dir), 0.0);*/dot(norm, dir); + /* if (CosTheta == 0.0) { + return 0.0; + } + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + float TanTheta = SinTheta / CosTheta; + float absTanTheta = abs(TanTheta); */ + // vec3 w = normalize(dir - dot(dir, norm) * (norm)); + // float CosTheta = w.z; + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + float TanTheta = SinTheta / CosTheta; + float absTanTheta = abs(TanTheta); + /* if (isinf(absTanTheta)) { + return 0.0; + } */ + /* float CosPhi = mix(clamp(projDirNorm.x / sinTheta, -1.0, 1.0), 0.0, sinTheta == 0.0); + float SinPhi = mix(clamp(projDirNorm.y / sinTheta, -1.0, 1.0), 0.0, sinTheta == 0.0); + float alpha = sqrt(CosPhi * CosPhi * alphax * alphax + SinPhi * SinPhi * alphay * alphay); */ + // Float absTanTheta = std::abs(TanTheta(w)); + // if (std::isinf(absTanTheta)) return 0.; + // <> + // Float alpha = std::sqrt(Cos2Phi(w) * alphax * alphax + + // Sin2Phi(w) * alphay * alphay); + float a = 1.0 / (alpha * absTanTheta); + /* if (a >= 1.6) { + return 0.0; + } + + return (1.0 - 1.259 * a + 0.396 * a * a) / (3.535 * a + 2.181 * a * a); */ + + return mix(max(0.0, (1.0 - 1.259 * a + 0.396 * a * a) / (3.535 * a + 2.181 * a * a)), 0.0, isinf(absTanTheta) || a >= 1.6); + // Float a = 1 / (alpha * absTanTheta); + // if (a >= 1.6f) + // return 0; + // return (1 - 1.259f * a + 0.396f * a * a) / + // (3.535f * a + 2.181f * a * a); + // return 1 / (1 + Lambda(wo) + Lambda(wi)); +} + +float BeckmannDistribution_G(vec3 norm, vec3 dir, vec3 light_dir, float alpha) { + // return 1 / (1 + Lambda(wo) + Lambda(wi)); + return 1.0 / (1.0 + BeckmannDistribution_Lambda(norm, dir, alpha) + BeckmannDistribution_Lambda(norm, -light_dir, alpha)); +} + +// Fresnel blending +// +// 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_f(vec3 norm, vec3 dir, vec3 light_dir, vec3 R_d, vec3 R_s, float alpha) { + const float PI = 3.1415926535897932384626433832795; + alpha = alpha * sqrt(2.0); + float cos_wi = /*max(*/dot(-light_dir, norm)/*, 0.0)*/; + float cos_wo = /*max(*/dot(dir, norm)/*, 0.0)*/; + + vec3 diffuse = (28.0 / (23.0 * PI)) * R_d * + (1.0 - R_s) * + (1.0 - pow5(1.0 - 0.5 * abs(cos_wi))) * + (1.0 - pow5(1.0 - 0.5 * abs(cos_wo))); + /* Spectrum diffuse = (28.f/(23.f*Pi)) * Rd * + (Spectrum(1.f) - Rs) * + (1 - pow5(1 - .5f * AbsCosTheta(wi))) * + (1 - pow5(1 - .5f * AbsCosTheta(wo))); */ + // Vector3f wh = wi + wo; + vec3 wh = -light_dir + dir; +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + bool is_blocked = cos_wi == 0.0 || cos_wo == 0.0; +#else + bool is_blocked = cos_wi <= 0.0 || cos_wo <= 0.0; +#endif + if (is_blocked) { + return vec3(/*diffuse*/0.0); + } + // if (cos_wo < 0.0) { + // return /*vec3(0.0)*/diffuse; + // } + /* if (cos_wi == 0.0 || cos_wo == 0.0) { + return vec3(0.0); + } */ + /* if (wh.x == 0 && wh.y == 0 && wh.z == 0) { + return vec3(0.0); + // return Spectrum(0); + } */ + wh = normalize(wh);//mix(normalize(wh), vec3(0.0), equal(light_dir, dir)); + float dot_wi_wh = dot(-light_dir, wh); + vec3 specular = BeckmannDistribution_D(dot(wh, norm), alpha) / + (4 * abs(dot_wi_wh) * + max(abs(cos_wi), abs(cos_wo))) * + schlick_fresnel(R_s, dot_wi_wh); + // Spectrum specular = distribution->D(wh) / + // (4 * AbsDot(wi, wh) * + // std::max(AbsCosTheta(wi), AbsCosTheta(wo))) * + // SchlickFresnel(Dot(wi, wh)); + return mix(/*diffuse*//* + specular*/diffuse + specular, vec3(0.0), bvec3(all(equal(light_dir, dir)))); +} + +// Fresnel blending +// +// 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, 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)*/; + float cos_wo = /*max(*/dot(dir, norm)/*, 0.0)*/; + +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + vec4 AbsNdotL = abs(vec4(light_dir, cos_wi)); + vec4 AbsNdotV = abs(vec4(dir, cos_wo)); +#else + 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 + + // float R_r = 1.0 - R_s; + // float R_r = 1.0 - schlick_fresnel(R_s, cos_wi); + // // Rs + pow5(1.0 - cosTheta) * (1.0 - Rs) + // vec4 R_r = 1.0 - (R_s + (1.0 - R_s) * schlick_fresnel(R_s, cos_wi)); + // mat4 R_r = 1.0 - (vec4(R_s, 0.0) + vec4(1.0 - R_s, 0.0) * pow5(1.0 - AbsNdotL)); + // vec4 AbsNdotL5 = pow5(1.0 - AbsNdotL); + // vec4 R_s4 = vec4(R_s, 0.0); + // mat4 R_r = + // // mat4(1.0 - (R_s.r + (1.0 - R_s.r) * AbsNdotL5), + // // 1.0 - (R_s.g + (1.0 - R_s.g) * AbsNdotL5), + // // 1.0 - (R_s.b + (1.0 - R_s.b) * AbsNdotL5), + // // vec4(0.0) + // // ); + // mat4(1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.x), + // 1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.y), + // 1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.z), + // 1.0 - (R_s4 + (1.0 - R_s4) * AbsNdotL5.w) + // ); + // * ) (R1.0 - R_s.r) 1.0 - (vec4(R_s, 0.0) + vec4(1.0 - R_s, 0.0) * pow5(1.0 - AbsNdotL)); + + vec4 diffuse_factor = + // vec4(abs(vec4(-light_dir * sides, cos_wi))) + (1.0 - pow5(1.0 - 0.5 * AbsNdotL)) * + // (1.0 - pow5(1.0 - 0.5 * abs(vec4(-light_dir * sides, cos_wi)))) * + // (1.0 - pow5(1.0 - 0.5 * abs(vec4(dir * sides, cos_wo)))) + (1.0 - pow5(1.0 - 0.5 * AbsNdotV)) + // vec4(1.0) + ; + /* vec4 diffuse_factor = + (1.0 - pow5(1.0 - 0.5 * max(vec4(-light_dir * sides, abs(cos_wi)), 0.0))) * + (1.0 - pow5(1.0 - 0.5 * max(vec4(dir * sides, abs(cos_wo)), 0.0))); */ + + vec3 diffuse = (28.0 / (23.0 * PI))/*(1.0 / PI)*/ * R_d * + (1.0 - R_s) * + //vec3( + dot(diffuse_factor, /*R_r * */vec4(abs(norm) * (1.0 - dist), dist)) + //) + ; + + vec3 wh = -light_dir + dir; +#if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + bool is_blocked = cos_wi == 0.0 || cos_wo == 0.0; +#else + bool is_blocked = cos_wi <= 0.0 || cos_wo <= 0.0; +#endif + if (is_blocked) { + return vec3(/*diffuse*/0.0); + } + 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, 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) * + max(abs(cos_wi), abs(cos_wo))) * + schlick_fresnel(R_s, dot_wi_wh); + return mix(/*diffuse*//* + specular*/diffuse + specular, vec3(0.0), bvec3(all(equal(light_dir, dir)))); +} + +// Phong reflection. +// +// Note: norm, dir, light_dir must all be normalizd. +vec3 light_reflection_factor2(vec3 norm, vec3 dir, vec3 light_dir, vec3 k_d, vec3 k_s, float alpha) { + // TODO: These are supposed to be the differential changes in the point location p, in tangent space. + // That is, assuming we can parameterize a 2D surface by some function p : R² → R³, mapping from + // points in a plane to 3D points on the surface, we can define + // ∂p(u,v)/∂u and ∂p(u,v)/∂v representing the changes in the pont location as we move along these + // coordinates. + // + // Then we can define the normal at a point, n(u,v) = ∂p(u,v)/∂u × ∂p(u,v)/∂v. + // + // Additionally, we can define the change in *normals* at each point using the + // Weingarten equations (see http://www.pbr-book.org/3ed-2018/Shapes/Spheres.html): + // + // ∂n/∂u = (fF - eG) / (EG - F²) ∂p/∂u + (eF - fE) / (EG - F²) ∂p/∂v + // ∂n/∂v = (gF - fG) / (EG - F²) ∂p/∂u + (fF - gE) / (EG - F²) ∂p/∂v + // + // where + // + // E = |∂p/∂u ⋅ ∂p/∂u| + // F = ∂p/∂u ⋅ ∂p/∂u + // G = |∂p/∂v ⋅ ∂p/∂v| + // + // and + // + // e = n ⋅ ∂²p/∂u² + // f = n ⋅ ∂²p/(∂u∂v) + // g = n ⋅ ∂²p/∂v² + // + // For planes (see http://www.pbr-book.org/3ed-2018/Shapes/Triangle_Meshes.html) we have + // e = f = g = 0 (since the plane has no curvature of any sort) so we get: + // + // ∂n/∂u = (0, 0, 0) + // ∂n/∂v = (0, 0, 0) + // + // To find ∂p/∂u and ∂p/∂v, we first write p and u parametrically: + // p(u, v) = p0 + u ∂p/∂u + v ∂p/∂v + // + // ( u₀ - u₂ v₀ - v₂ + // u₁ - u₂ v₁ - v₂ ) + // + // Basis: plane norm = norm = (0, 0, 1), x vector = any orthgonal vector on the plane. + // vec3 w_i = + // vec3 w_i = vec3(view_mat * vec4(-light_dir, 1.0)); + // vec3 w_o = vec3(view_mat * vec4(light_dir, 1.0)); + float g = 1.0;// BeckmannDistribution_G(norm, dir, light_dir, alpha); + return FresnelBlend_f(norm, dir, light_dir, k_d/* * max(dot(norm, -light_dir), 0.0)*/, k_s * g, alpha); + // const float PI = 3.141592; + // alpha = alpha * sqrt(2.0); + // float ndotL = /*max*/(dot(norm, -light_dir)/*, 0.0*/); + + // //if (ndotL > 0.0/* && dot(s_norm, -light_dir) > 0.0*/) { + // vec3 H = normalize(-light_dir + dir); + + // float NdotH = dot(norm, H); + // float NdotH2 = NdotH * NdotH; + // float NdotH2m2 = NdotH2 * alpha * alpha; + // float k_spec = exp((NdotH2 - 1) / NdotH2m2) / (PI * NdotH2m2 * NdotH2); + // return mix(k_s * k_spec, vec3(0.0), bvec3(ndotL <= 0.0 || NdotH == 0.0)); + // // + // // (k_d * (L ⋅ N) + k_s * (R ⋅ V)^α) + // // return k_d * ndotL + mix(k_s * pow(max(dot(norm, H), 0.0), alpha * 4.0), vec3(0.0), bvec3(ndotL == 0.0)); + // // } + // // return vec3(0.0); +} + +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 + vec4 AbsNdotL = abs(vec4(light_dir, dot(norm, light_dir))); + #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(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 + float diffuse = max(dot(norm, -light_dir), 0.0); + #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 + float ndotL = abs(dot(norm, light_dir)); + #else + float ndotL = max(dot(norm, -light_dir), 0.0); + #endif + + if (ndotL > 0.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(voxel_norm); + vec4 AbsNdotL = max(vec4(-light_dir * sides, ndotL), 0.0); + #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 + vec3 H = normalize(-light_dir + dir); + + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + float NdotH = abs(dot(norm, H)); + #else + float NdotH = max(dot(norm, H), 0.0); + #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_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 +} + +float rel_luminance(vec3 rgb) +{ + // https://en.wikipedia.org/wiki/Relative_luminance + const vec3 W = vec3(0.2126, 0.7152, 0.0722); + return dot(rgb, W); +} + +// From https://discourse.vvvv.org/t/infinite-ray-intersects-with-infinite-plane/10537 +// out of laziness. +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; +} + +// Compute uniform attenuation due to beam passing through a substance that fills an area below a horizontal plane +// (e.g. in most cases, water below the water surface depth) using the simplest form of the Beer-Lambert law +// (https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law): +// +// I(z) = I₀ e^(-μz) +// +// We compute this value, except for the initial intensity which may be multiplied out later. +// +// wpos is the position of the point being hit. +// ray_dir is the reversed direction of the ray (going "out" of the point being hit). +// mu is the attenuation coefficient for R, G, and B wavelenghts. +// surface_alt is the estimated altitude of the horizontal surface separating the substance from air. +// defaultpos is the position to use in computing the distance along material at this point if there was a failure. +// +// Ideally, defaultpos is set so we can avoid branching on error. +vec3 compute_attenuation(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) { +#if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE) + return vec3(1.0); +#elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE) + #if (LIGHTING_TYPE & LIGHTING_TYPE_TRANSMISSION) != 0 + return vec3(1.0); + #else + // return vec3(1.0); + /*if (mu == vec3(0.0)) { + return vec3(1.0); + }*//* else { + return vec3(0.0); + }*/ + // return vec3(0.0); + // vec3 surface_dir = /*surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0)*/vec3(0.0, 0.0, sign(surface_alt - wpos.z)); + ray_dir = faceforward(ray_dir, vec3(0.0, 0.0, -1.0), ray_dir); + vec3 surface_dir = surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0); + // vec3 surface_dir = faceforward(vec3(0.0, 0.0, 1.0), ray_dir, vec3(0.0, 0.0, 1.0)); + bool _intersects_surface = IntersectRayPlane(wpos, ray_dir, vec3(0.0, 0.0, surface_alt), surface_dir, defaultpos); + float depth = length(defaultpos - wpos); + return exp(-mu * depth); + #endif +#endif +} + +// vec3 compute_attenuation2(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) { +// #if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE) +// return vec3(1.0); +// #elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE) +// // return vec3(1.0); +// /*if (mu == vec3(0.0)) { +// return vec3(1.0); +// }*//* else { +// return vec3(0.0); +// }*/ +// // return vec3(0.0); +// // vec3 surface_dir = /*surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0)*/vec3(0.0, 0.0, sign(surface_alt - wpos.z)); +// vec3 surface_dir = surface_alt < wpos.z ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); +// // vec3 surface_dir = faceforward(vec3(0.0, 0.0, 1.0), ray_dir, vec3(0.0, 0.0, 1.0)); +// bool _intersects_surface = IntersectRayPlane(wpos, ray_dir, vec3(0.0, 0.0, surface_alt), surface_dir, defaultpos); +// float depth = length(defaultpos - wpos); +// return exp(-mu * depth); +// #endif +// } + +// Same as compute_attenuation but since both point are known, set a maximum to make sure we don't exceed the length +// from the default point. +vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) { +#if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE) + return vec3(1.0); +#elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE) + // return vec3(1.0); + /*if (mu == vec3(0.0)) { + return vec3(1.0); + }*//* else { + return vec3(0.0); + }*/ + // return vec3(0.0); + vec3 surface_dir = /*surface_alt < wpos.z ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 0.0, 1.0)*/vec3(0.0, 0.0, sign(wpos.z - surface_alt)); + // vec3 surface_dir = surface_alt < wpos.z ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + // vec3 surface_dir = faceforward(vec3(0.0, 0.0, 1.0), ray_dir, vec3(0.0, 0.0, 1.0)); + float max_length = dot(defaultpos - wpos, defaultpos - wpos); + bool _intersects_surface = IntersectRayPlane(wpos, ray_dir, vec3(0.0, 0.0, surface_alt), surface_dir, defaultpos); + float depth2 = min(max_length, dot(defaultpos - wpos, defaultpos - wpos)); + 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 new file mode 100644 index 0000000000..ee3f84070b --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-frag.glsl @@ -0,0 +1,49 @@ +// 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); + // // float lightDistance = length(FragPos - lights[((/*FragLayer*/1 - 1) & 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-geom.glsl b/assets/voxygen/shaders/light-shadows-geom.glsl new file mode 100644 index 0000000000..78e0b0896e --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-geom.glsl @@ -0,0 +1,277 @@ +// Adapted from https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows + +// NOTE: We only technically need this for cube map arrays and geometry shader +// instancing. +#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 the max light count (light_shadow_count.x) +// and the far plane (scene_res.z). +#include +#include +// // Currently, we only need lights for the light position +// #include + +/* struct Light { + vec4 light_pos; + vec4 light_col; + // mat4 light_proj; +}; + +layout (std140) +uniform u_lights { + Light lights[31]; +}; */ + +// Since our output primitive is a triangle strip, we have to render three vertices +// each. +#define VERTICES_PER_FACE 3 + +// Since we render our depth texture to a cube map, we need to render each face +// six times. If we used other shadow mapping methods with fewer outputs, this would +// shrink considerably. +#define FACES_PER_POINT_LIGHT 6 + +// If MAX_VERTEX_UNIFORM_COMPONENTS_ARB = 512 on many platforms, and we want a mat4 +// for each of 6 directions for each light, 20 is close to the maximum allowable +// size. We could add a final matrix for the directional light of the sun or moon +// to bring us to 126 matrices, which is just 2 off. +// +// To improve this limit, we could do many things, such as: +// - choose an implementation that isn't cube maps (e.g. tetrahedrons or curves; +// if there were an easy way to sample from tetrahedrons, we'd be at 32 * 4 = 128 +// exactly, leaving no room for a solar body, though). +// - Do more work in the geometry shader (e.g. just have a single projection +// matrix per light, and derive the different-facing components; or there may be +// other ways of greatly simplifying this). The tradeoff would be losing performance +// here. +// - Use ARB_instanced_arrays and switch lights with indexing, instead of a uniform +// buffer. This would probably work fine (and ARB_instanced_arrays is supported on +// pretty much every platform), but AFAIK it's possible that instanced arrays are +// slower than uniform arraay access on many platforms. +// - Don't try to do everything in one call (break this out into multiple passes). +// +// Actually, according to what I'm reading, MAX_GEOM_UNIFORM_COMPONENTS = 1024, and +// gl_MaxGeometryUniformComponents = 1024. +// +// Also, this only applies to uniforms defined *outside* of uniform blocks, of which +// there can be up to 12 (14 in OpenGL 4.3, which we definitely can't support). +// GL_MAX_UNIFORM_BLOCK_SIZE has a minimum of 16384, which *easily* exceeds our usage +// constraints. So this part might not matter. +// +// Other restrictions are easy to satisfy: +// +// gl_MaxGeometryVaryingComponents has a minimum of 64 and is the maximum number of +// varying components; I think this is the number of out components per vertex, which +// is technically 0, but would be 4 if we wrote FragPos. But it might also +// be the *total* number of varying components, in which case if we wrote FragPos +// it would be 4 * 20 * 6 * 3 = 1440, which would blow it out of the water. However, +// I kind of doubt this interpretation because writing FragPos for each of 18 vertices, +// as the original shader did, already yields 4 * 18 = 72, and it seems unlikely that +// the original example exceeds OpenGL limits. +// +// gl_MaxGeometryOutputComponents has a minimum of 128 and is the maximum number of +// components allowed in out variables; we easily fall under this since we actually +// have 0 of these. However, if we were to write FragPos for each vertex, it *might* +// cause us to exceed this limit, depending on whether it refers to the total output +// component count *including* varying components, or not. See the previous +// discussion; since 72 < 128 it's more plausible that this interpretation might be +// correct, but hopefully it's not. +// +// gl_MaxGeometryInputComponents has a minimum of 64 and we easily fall under that +// limit (I'm actually not sure we even have any user-defined input components?). +// +// gl_MaxGeometryTextureImageUnits = 16 and we have no texture image units (or maybe +// 1, the one we bound?). This might come into play if we were to have attached +// cubemaps instead of a single cubemap array, in which case it would limit us to +// 16 lights *regardless* of any of the fixes mentioned above (i.e., we'd just have +// to split up draw calls, I think). +// +// --- +// +// However, there is another limit to consider: GL_MAX_GEOMETRY_OUTPUT_VERTICES. Its +// minimum is 256, and 20 * 6 * 3 = 360, which exceeds that. This introduces a new +// limit of at most 14 point lights. +// +// Another, related limit is GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS. This counts +// every component output ("component" is usually a 4-byte field of a vector, but maybe +// this would improve with something like half-floats?), and has a minimum (as of +// OpenGL 3.3) of 1024. Since even builtin outputs gl_Layer count against this total, +// this means we issue 5 components per vertex, and 14 * 6 * 3 * 5 = 1260 > 1024. +// +// Ultimately, we find our maximum output limit of 11, ≤ 1024/5/3/6. +// +// If we choose to reserve a slot for a non-point light (and/or other uniforms), it +// is just 10, or half what we got from VERTICES_PER_FACE (we could also round down to +// 8 as a power of 2, if we had to). +// +// Unlike the input limits, whwich we can get around with "clever" solutions, it seems +// likely that the only real way to defeat the vertex limits is to use instancing of +// some sort (be it geometry shader or otherwise). This would restrict us to OpenGL +// 4.0 or above. +// +// A further consideration (were we to switch to OpenGL 4.1-supported features, but +// actually it is often supported on 3.3 hardware with ARB_viewport_array--whereas +// geometry shader instancing is *not* supported on any 3.3 hardware, so would actually +// require us to upgrade) would be setting gl_ViewportIndex. The main reason to consider +// this is that it allows specifying a separate scissor rectangle per viewport. This +// introduces two new constraints. Firstly, it adds an extra component to each vertex +// (lowering our maximum to 9 faces per light ≤ 1024/6/3/6, or 8 if we want to support a +// directional light). +// +// Secondly, a new constant (MAX_VIEWPORTS) is introduced, which would restrict the +// total number of active viewports; the minimum value for this is 16. While this may +// not seem all that relevant since our current hard limit is 11, the difference is that +// this limit would apply *across* instanced calls (since it may be a "global" +// restriction, tied to the OpenGL context; this means it couldn't even be a multiple +// frame buffer thing, as there is usually one per window). This would also tie in +// with gl_MaxGeometryTextureImageUnits, I guess. +// +// -- +// +// I just realized tht using cube map arrays at all bumps our required OpenGL +// version to 4.0, so let's just do instancing... +// +// The instancing limit on MAX_GEOMETRY_SHADER_INVOCATIONS has a minimum of 32, which +// would be sufficient to run through all 32 lights with a different cube map and +// completely removes any imits on ight count. +// +// This should instantly bring us below all relevant limits in all cases considered +// except for the two that would require 16. Unfortunately, 32 is also the *maximum* +// number of point lights, which is much higher than the usual value, and the instance +// count has to be a constant. If we were to instead geometry-shader-instance each +// *face*, we'd get a maximum light count of 56 ≤ 1024/6/3, which is not as elegant +// but is easily higher than 32. So, let's try using that instead. +// +// It is *possible* that using instancing on the *vertex* shader with the (dynamically +// uniform) total number of instances set to the actual number of point lights, would +// improve performance, since it would give us a 1:1 vertex input:output ratio, which +// might be optimized in hardware. +// +// It also seems plausible that constructing a separate geometry shader with values +// from 1 to 32 would be worthwhile, but that seems a little extreme. +// +// --- +// +// Since wgpu doesn't support geometry shaders anyway, it seems likely that we'll have +// to do the multiple draw calls, anyway... I don't think gl_Layer can be set from +// outside a geometry shader. But in wgpu, such a thing is much cheaper, anyway. +#define MAX_POINT_LIGHTS 31 + +// We use geometry shader instancing to construct each face separately. +#define MAX_LAYER_VERTICES_PER_FACE (MAX_POINT_LIGHTS * VERTICES_PER_FACE) + +#define MAX_LAYER_FACES (MAX_POINT_LIGHTS * FACES_PER_POINT_LIGHT) + +layout (triangles/*, invocations = 6*/) in; + +layout (triangle_strip, max_vertices = /*MAX_LAYER_VERTICES_PER_FACE*//*96*/18) out; + +//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 +// relaxed (unless the total of all our varying output components can't exceed +// 128, which would mean FragPos would sum to 4 * 3 * 32 = 384; this could be +// remedied only by setting MAX_POINT_LIGHTS to ), we might enable it again soon. +// +// out vec3 FragPos; // FragPos from GS (output per emitvertex) +// flat out int FragLayer; // Current layer + +// 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)); + +void main() { + // return; + // NOTE: Assuming that light_shadow_count.x < MAX_POINT_LIGHTS. We could min + // it, but that might make this less optimized, and I'd like to keep this loop as + // optimized as is reasonably possible. + // int face = gl_InvocationID; + + // Part 1: emit directed lights. + /* if (face <= light_shadow_count.z) { + // Directed light. + for(int i = 0; i < VERTICES_PER_FACE; ++i) // for each triangle vertex + { + // NOTE: See above, we don't make FragPos a uniform. + FragPos = gl_in[i].gl_Position; + FragLayer = 0; // 0 is the directed light layer. + // vec4 FragPos = gl_in[i].gl_Position; + gl_Layer = i; // built-in variable that specifies to which face we render. + gl_Position = shadowMats[i].shadowMatrices * FragPos; + EmitVertex(); + } + EndPrimitive(); + } */ + + // Part 2: emit point lights. + /* if (light_shadow_count.x == 1) { + return; + } */ +#if (SHADOW_MODE == SHADOW_MODE_MAP) + for (uint layer = 1u; layer <= min(light_shadow_count.x, 1u); ++layer) + { + 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. + vec3 fragPos = gl_in[i].gl_Position.xyz; + // FragPos = fragPos - (lights[((/*FragLayer*/layer - 1u) & 31u)].light_pos.xyz - focus_off.xyz); + // FragLayer = layer; + // float lightDistance = length(FragPos - lights[((layer - 1) & 31)].light_pos.xyz); + // lightDistance /= screen_res.w; + + // vec4 FragPos = gl_in[i].gl_Position; + // NOTE: Our normals map to the same thing as cube map normals, *except* that their normal direction is + // swapped; we can fix this by doing normal ^ 0x1u. However, we also want to cull back faces, not front + // faces, so we only care about the shadow cast by the *back* of the triangle, which means we ^ 0x1u + // again and cancel it out. + // int face = int(((floatBitsToUint(gl_Position.w) >> 29) & 0x7u) ^ 0x1u); + int layer_face = layer_base + face; + gl_Layer = face;//layer_face; // built-in variable that specifies to which face we render. + gl_Position = shadowMats[layer_face].shadowMatrices * vec4(fragPos, 1.0); + // gl_Position.z = -((gl_Position.z + screen_res.z) / (screen_res.w - screen_res.z)) * lightDistance; + // gl_Position.z = gl_Position.z / screen_res.w; + // gl_Position.z = gl_Position.z / gl_Position.w; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + // lightDistance = -(lightDistance + screen_res.z) / (screen_res.w - screen_res.z); + // gl_Position.z = lightDistance; + EmitVertex(); + } + EndPrimitive(); + } + } +#endif +} diff --git a/assets/voxygen/shaders/light-shadows-vert.glsl b/assets/voxygen/shaders/light-shadows-vert.glsl new file mode 100644 index 0000000000..4ba3c77f9d --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-vert.glsl @@ -0,0 +1,55 @@ +#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 focus_off. +#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() { + vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; + // f_pos = v_pos; + // vec3 f_pos = f_chunk_pos + model_offs; + + // gl_Position = v_pos + vec4(model_offs, 0.0); + gl_Position = /*all_mat * */vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); + // shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex; + // vec4(v_pos, 0.0, 1.0); +} diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl new file mode 100644 index 0000000000..8ae0f0ab2c --- /dev/null +++ b/assets/voxygen/shaders/lod-terrain-frag.glsl @@ -0,0 +1,632 @@ +#version 330 core + +#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_VOXEL +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_LOD_FULL_INFO + +#include +#include +#include + +in vec3 f_pos; +in vec3 f_norm; +// 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); + + // 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); */ + vec3 my_pos = vec3(f_pos.xy, my_alt); + 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; + // 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 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); + /* if (length(f_pos - cam_pos.xyz) <= view_distance.x + 32.0) { + vec4 new_f_pos; + float depth = 10000000.0; + vec4 old_coord = all_mat * vec4(f_pos.xyz, 1.0); + for (int i = 0; i < 6; i ++) { + // vec4 square = focus_pos.xy + vec4(splay(pos - vec2(1.0, 1.0), splay(pos + vec2(1.0, 1.0)))); + vec3 my_f_norm = normals[i]; + vec3 my_f_tan = normals[(i + 2) % 6]; + vec3 my_f_bitan = normals[(i + 4) % 6]; + mat4 foo = mat4(vec4(my_f_tan, 0), vec4(my_f_bitan, 0), vec4(my_f_norm, 0), vec4(0, 0, 0, 1)); + mat4 invfoo = foo * inverse(foo * all_mat); + vec4 my_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0); + vec4 my_f_proj = all_mat * my_f_pos; + if (my_f_proj.z <= depth) { + new_f_pos = my_f_pos; + f_norm = my_f_norm; + depth = my_f_proj.z; + } + } + // f_pos = new_f_pos.xyz; + } */ + + // Test for distance to all 6 sides of the enclosing cube. + // if (/*any(lessThan(fract(f_pos.xy), 0.01))*/fract_pos.x <= 0.1) { + // f_norm = faceforward(vec3(-1, 0, 0), f_norm, vec3(1, 0, 0)); + // f_tan = vec3(0, 1, 0); + // } else if (fract_pos.y <= 0.1) { + // f_norm = faceforward(vec3(0, -1, 0), f_norm, vec3(0, 1, 0)); + // f_tan = vec3(0, 0, 1); + // } else { + // f_norm = faceforward(vec3(0, 0, -1), f_norm, vec3(0, 0, 1)); + // f_tan = vec3(1, 0, 0); + // } + // vec3 f_bitan = cross(f_norm, f_tan); + + // mat4 foo = mat4(vec4(f_tan, 0), vec4(f_bitan, 0), vec4(f_norm, 0), vec4(0, 0, 0, 1)); + // 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); + // 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); + + // const vec3 normals[3] = 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)); + // const mat3 side_norms = vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1); + // mat3 sides = mat3( + // /*vec3(1, 0, 0), + // vec3(0, 1, 0), + // vec3(0, 0, 1)*/ + // vec3(1, 0, 0), + // // faceforward(vec3(1, 0, 0), -f_norm, vec3(1, 0, 0)), + // vec3(0, 1, 0), + // // faceforward(vec3(0, 1, 0), -f_norm, vec3(0, 1, 0)), + // vec3(0, 0, 1) + // // faceforward(vec3(0, 0, 1), -f_norm, vec3(0, 0, 1)) + // ); + + // This vector is shorthand for a diagonal matrix, which works because: + // (1) our voxel normal vectors are exactly the basis vectors in worldspace; + // (2) only 3 of them can be in the direction of the actual normal anyway. + // (NOTE: This normal should always be pointing up, so implicitly sides.z = 1.0). + // vec3 sides = sign(f_norm); + // // NOTE: Should really be sides * f_norm, i.e. abs(f_norm), but voxel_norm would then re-multiply by sides so it cancels out. + // vec3 cos_sides_i = sides * f_norm; + // vec3 cos_sides_o = sides * view_dir; + // // vec3 side_factor_i = cos_sides_i; + // // vec3 side_factor_i = f_norm; + // // vec3 side_factor_i = cos_sides_o; + // vec3 side_factor_i = 1.0 - pow(1.0 - 0.5 * cos_sides_i, vec3(5)); + // // vec3 side_factor_i = /*abs*/sign(f_norm) * cos_sides_i;//max(cos_sides_i, 0.0);// 1.0 - pow(1.0 - 0.5 * cos_sides_i, vec3(5.0)); // max(sides * f_norm, vec3(0.0));// + // // vec3 side_factor_i = /*abs*/sign(f_norm) * cos_sides_i;//max(cos_sides_i, 0.0);// 1.0 - pow(1.0 - 0.5 * cos_sides_i, vec3(5.0)); // max(sides * f_norm, vec3(0.0));// + // // vec3 side_factor_o = max(cos_sides_o, 0.0);// 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, 0.0), vec3(5)); + // vec3 side_factor_o = 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, 0.0), vec3(5)); + // // vec3 side_factor_o = max(cos_sides_o, 0.0);// 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, vec3(0.0)), vec3(5.0));//max(sides * view_dir/* * sign(cos_sides_i) */, vec3(0.0)); + // // vec3 side_factor_o = max(sides * view_dir/* * cos_sides_o*/, 0.0);// 1.0 - pow(1.0 - 0.5 * max(cos_sides_o, vec3(0.0)), vec3(5.0));//max(sides * view_dir/* * sign(cos_sides_i) */, vec3(0.0)); + // // NOTE: side = transpose(sides), so we avoid the extra operatin. + // // We multply the vector by the matrix from the *left*, so each normal gets multiplied by the corresponding factor. + // // vec3 voxel_norm = normalize(/*sides * *//*sqrt(1.0 - cos_sides_i * cos_sides_i)*/(side_factor_i * side_factor_o)); + // vec3 voxel_norm = normalize(/*sides * *//*sqrt(1.0 - cos_sides_i * cos_sides_i)*/((28.0 / (23.0 * PI)) * side_factor_i * side_factor_o * sides)); + // vec3 voxel_norm = normalize(sign(f_norm) * sqrt(abs(f_norm)) * max(sign(f_norm) * view_dir, 0.0)); + float f_ao = 1.0;//1.0;//sqrt(dot(cos_sides_i, cos_sides_i) / 3.0); + // float f_ao = 0.2; + // sqrt(dot(sqrt(1.0 - cos_sides_i * cos_sides_i)), 1.0 - cos_sides_o/* * cos_sides_o*/);// length(sqrt(1.0 - cos_sides_o * cos_sides_o) / cos_sides_i * cos_sides_o); + // f_ao = f_ao * f_ao; + + // /* vec3 voxel_norm = vec3(0.0); + // for (int i = 0; i < 3; i ++) { + // // Light reflecting off the half-angle can shine on up to three sides. + // // So, the idea here is to figure out the ratio of visibility of each of these + // // three sides such that their sum adds to 1, then computing a Beckmann Distribution for each side times + // // the this ratio. + // // + // // The ratio of these normals in each direction should be the sum of their cosines with the light over π, + // // I think. + // // + // // cos (wh, theta) + // // + // // - one normal + // // + // // The ratio of each of the three exposed sides should just be the slope. + // vec3 side = normals[i]; + // side = faceforward(side, -f_norm, side); + // float cos_wi = max(dot(f_norm, side), 0.0); + // float cos_wo = max(dot(view_dir, side), 0.0); + // float share = cos_wi * cos_wo; + // // float share = (1.0 - pow5(1.0 - 0.5 * cos_wi)) * (1.0 - pow5(1.0 - 0.5 * cos_wo)); + // voxel_norm += share * side; + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // // voxel_norm += normals[i] * side_visible * max(dot(-cam_dir, normals[i]), 0.0); + // } + // voxel_norm = normalize(voxel_norm); */ + + 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)); + + // IDEA: + // We can represent three faces as sign(voxel_norm). + vec3 sides = sign(f_norm); + // There are three relevant vectors: normal, tangent, and bitangent. + // We say normal is the z component, tangent the x component, bitangent the y. + // A blocking side is in the reverse direction of each. + // So -sides is the *direction* of the next block. + // Now, we want to multiply this by the *distance* to the nearest integer in that direction. + // If sides.x is -1, the direction is 1, so the distance is 1.0 - fract(f_pos.x) and the delta is 1.0 - fract(f_pos.x). + // If sides.x is 1, the direction is -1, so the distance is fract(f_pos.x) and the delta is -fract(f_pos.x) = 1.0 + fract(-f_pos.x). + // If sides.x is 0, the direction is 0, so the distance is 0.0 and the delta is 0.0 = 0.0 + fract(0.0 * f_pos.x). + // (we ignore f_pos < 0 for the time being). + // Then this is 1.0 + sides.x * fract(-sides.x * f_pos.x); + // We repeat this for y. + // + // We treat z as the dependent variable. + // IF voxel_norm.x > 0.0, z should increase by voxel_norm.z / voxel_norm.x * delta_sides.x in the x direction; + // IF voxel_norm.y > 0.0, z should increase by voxel_norm.z / voxel_norm.y * delta_sides.y in the y direction; + // IF voxel_norm.x = 0.0, z should not increase in the x direction; + // IF voxel_norm.y = 0.0, z should not increase in the y direction; + // we assume that ¬(voxel_norm.z = 0). + // + // Now observe that we can rephrase this as saying, given a desired change in z (to get to the next integer), how far must + // 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( + 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); + vec2 corner_yz = min(abs(f_norm.yz / f_norm.x * delta_sides.x), 1.0); + vec2 corner_xz = min(abs(f_norm.xz / f_norm.y * delta_sides.y), 1.0); + // 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)); + // 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))*/); + 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(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))); + // f_ao = pow(f_ao_vec.x * f_ao_vec.y * f_ao_vec.z * 3.0, 1.0 / 2.0); // 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = pow(f_ao_vec.x * f_ao_vec.y * f_ao_vec.z, 1.0 / 3.0); // 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = f_ao_vec.x * f_ao_vec.y * f_ao_vec.z + (1.0 - f_ao_vec.x) * (1.0 - f_ao_vec.y) * (1.0 - f_ao_vec.z); + // f_ao = sqrt((f_ao_vec.x + f_ao_vec.y + f_ao_vec.z) / 3.0); // 1.0 / sqrt(3.0) * sqrt(dot(f_ao_vec, vec3(1.0))); + // f_ao = sqrt(dot(f_ao_vec, abs(voxel_norm))); + // f_ao = 3.0 / (1.0 / f_ao_vec.x + 1.0 / f_ao_vec.y + 1.0 / f_ao_vec.z); + // f_ao = min(ao_yz, min(ao_xz, ao_xy)); + // f_ao = max(f_ao_vec.x, max(f_ao_vec.y, f_ao_vec.z)); + // f_ao = min(f_ao_vec.x, min(f_ao_vec.y, f_ao_vec.z)); + // 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, sqrt(1.0 - delta_sides * delta_sides)); + // f_ao = dot(f_ao_vec, 1.0 - abs(delta_sides)); + // f_ao = + // 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) ? f_ao_vec.x : f_ao_vec.z : f_ao_vec.x : + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? f_ao_vec.y : f_ao_vec.z : f_ao_vec.y : + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) < abs(delta_sides.z) ? f_ao_vec.x : f_ao_vec.z : f_ao_vec.x : + // f_ao_vec.y < 1.0 ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.y) < abs(delta_sides.z) ? f_ao_vec.y : f_ao_vec.z : f_ao_vec.y : + // f_ao_vec.z; + // f_ao = abs(delta_sides.x) < abs(delta_sides.y) ? abs(delta_sides.x) < abs(delta_sides.z) ? f_ao_vec.x : f_ao_vec.z : + // abs(delta_sides.y) < abs(delta_sides.z) ? f_ao_vec.y : f_ao_vec.z; + // f_ao = abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.y) * f_ao_vec.y ? abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.z) * f_ao_vec.z ? f_ao_vec.x : f_ao_vec.z : + // 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 + 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 ? + // abs(delta_sides.x) < abs(delta_sides.y) ? + // /*f_ao_vec.z < 1.0 */true ? 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*/true ? 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*/true ? 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*/true ? + // /*f_ao_vec.z < 1.0*/true ? 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) : + // vec3(0.0, 0.0, sides.z); + /* 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 ? + // f_ao_vec.y < 1.0 ? + // abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.y) * f_ao_vec.y ? + // f_ao_vec.z < 1.0 ? abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.z) * f_ao_vec.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) * 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 ? abs(delta_sides.x) * f_ao_vec.x < abs(delta_sides.z) * f_ao_vec.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) * 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); + // voxel_norm = mix(voxel_norm, f_norm, dist_lerp); + + /* 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); + + // Magic stop-gap code without any physical justification. + vec3 lerpy_norm; + if (my_norm.z/*f_norm.z*/ > 0.99999) { + lerpy_norm = vec3(0, 0, 1); + } else { + vec3 side_norm = normalize(vec3(my_norm.xy, 0)); + // lerpy_norm = f_norm; + float mix_factor = clamp(abs(dot(f_orig_view_dir, side_norm)), 0, 1); + lerpy_norm = mix( + mix(my_norm, side_norm, clamp(dot(side_norm, my_norm) + 0.5, 0, 1)), + my_norm, + mix_factor + ); + } + const float DIST = 0.07; + voxel_norm = normalize(mix(voxel_norm, lerpy_norm, clamp(my_norm.z * my_norm.z - (1.0 - DIST), 0, 1) / DIST)); + + f_pos.xyz += abs(voxel_norm) * delta_sides; + voxel_norm = voxel_norm == vec3(0.0) ? f_norm : voxel_norm; + + vec3 hash_pos = f_pos + focus_off.xyz; + const float A = 0.055; + const float W_INV = 1 / (1 + A); + const float W_2 = W_INV * W_INV;//pow(W_INV, 2.4); + const float NOISE_FACTOR = 0.02;//pow(0.02, 1.2); + float noise = hash(vec4(floor(hash_pos * 3.0 - voxel_norm * 0.5), 0));//0.005/* - 0.01*/; + vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR); + // noise_delta = noise_delta * noise_delta * W_2 - f_col; + // lum = W ⋅ col + // lum + noise = W ⋅ (col + delta) + // W ⋅ col + noise = W ⋅ col + W ⋅ delta + // noise = W ⋅ delta + // delta = noise / W + // vec3 col = (f_col + noise_delta); + // vec3 col = noise_delta * noise_delta * W_2; + + f_col = noise_delta * noise_delta * W_2; + // f_col = /*srgb_to_linear*/(f_col + hash(vec4(floor(hash_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 = 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)); + // // f_ao = dot(abs(voxel_norm), f_ao_vec); + // // voxel_norm = f_norm; + + // Note: because voxels, we reduce the normal for reflections to just its z component, dpendng on distance to camera. + // Idea: the closer we are to facing top-down, the more the norm should tend towards up-z. + // vec3 l_norm; // = vec3(0.0, 0.0, 1.0); + // vec3 l_norm = normalize(vec3(f_norm.x / max(abs(f_norm.x), 0.001), f_norm.y / max(abs(f_norm.y), 0.001), f_norm.z / max(abs(f_norm.z), 0.001))); + // vec3 l_factor = 1.0 / (1.0 + max(abs(/*f_pos - cam_pos.xyz*//*-vec3(vert_pos4) / vert_pos4.w*/vec3(f_pos.xy, 0.0) - vec3(/*cam_pos*/focus_pos.xy, cam_to_frag)) - vec3(view_distance.x, view_distance.x, 0.0), 0.0) / vec3(32.0 * 2.0, 32.0 * 2.0, 1.0)); + // l_factor.z = + // vec4 focus_pos4 = view_mat * vec4(focus_pos.xyz, 1.0); + // vec3 focus_dir = normalize(-vec3(focus_pos4) / focus_pos4.w); + + // float l_factor = 1.0 - pow(clamp(0.5 + 0.5 * dot(/*-view_dir*/-cam_to_frag, l_norm), 0.0, 1.0), 2.0);//1.0 / (1.0 + 0.5 * pow(max(distance(/*focus_pos.xy*/vec3(focus_pos.xy, /*vert_pos4.z / vert_pos4.w*/f_pos.z), vec3(f_pos.xy, f_pos.z))/* - view_distance.x*/ - 32.0, 0.0) / (32.0 * 1.0), /*0.5*/1.0)); + // l_factor = 1.0; + // l_norm = normalize(mix(l_norm, f_norm, l_factor)); + // l_norm = f_norm; + + /* l_norm = normalize(vec3( + mix(l_norm.x, f_norm.x, clamp(pow(f_norm.x * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.y * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.z * 0.5, 64), 0, 1)) + )); */ + // f_norm = mix(l_norm, f_norm, min(1.0 / max(cam_to_frag, 0.001), 1.0)); + /* vec3 l_norm = normalize(vec3( + mix(-1.0, 1.0, clamp(pow(f_norm.x * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.y * 0.5, 64), 0, 1)), + mix(-1.0, 1.0, clamp(pow(f_norm.z * 0.5, 64), 0, 1)) + )); */ + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + 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); + // // 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; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float cam_alt = alt_at(cam_pos.xy); + 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 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. + vec3 cam_attenuation = compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos); + // 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; + // 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(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; + // reflected_light = vec3(0.0); + + // dot(diffuse_factor, /*R_r * */vec4(abs(norm) * (1.0 - dist), dist)) + + // corner_xy = mix(all(lessThan(corner_xy, 1.0)) ? vec2(0.0) : 0.4 * (), 1.0 + // + // TODO: Handle similar logic for z. + + // So we repeat this for all three sides to find the "next" position on each side. + // vec3 delta_sides = 1.0 + sides * fract(-sides * f_pos); + // Now, we + // Now, all we have to do is find out whether (again, assuming f_pos is positive) next_sides represents a new integer. + // We currently just treat this as "new floor != old floor". + + // So to find the position at the nearest voxel, we just subtract voxel_norm * fract(sides * ) from f_pos.z. + // Then to find out whether we meet a new "block" in 1 voxel, we just + // on the "other" side can be found (according to my temporary theory) as the cross product + // vec3 norm = normalize(cross( + // vec3(/*2.0 * SAMPLE_W*/square.z - square.x, 0.0, altx1 - altx0), + // 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), + // 1.0 + // //(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; + // otherwise, it counts as fractional AO. So what we need is to know whether the fractional AO to the next block in that direction pushes us to a new integer. + // + // vec3 ao_pos_z = floor(f_pos + f_norm); + // vec3 ao_pos_z = corner_distance; + // vec3 ao_pos = 0.5 - clamp(min(fract(abs(f_pos)), 1.0 - fract(abs(f_pos))), 0.0, 0.5); + // + // f_ao = /*sqrt*/1.0 - 2.0 * sqrt(dot(ao_pos, ao_pos) / 2.0); + // 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 = 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 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); + +#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)); + // color = surf_color; + + tgt_color = vec4(color, 1.0); +} diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl new file mode 100644 index 0000000000..6ef2dd7bdf --- /dev/null +++ b/assets/voxygen/shaders/lod-terrain-vert.glsl @@ -0,0 +1,104 @@ +#version 330 core + +#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_VOXEL + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#include +#include +#include + +in vec2 v_pos; + +layout (std140) +uniform u_locals { + vec4 nul; +}; + +out vec3 f_pos; +out vec3 f_norm; +// out vec2 v_pos_orig; +// out vec4 f_square; +// out vec4 f_shadow; +// out float f_light; + +void main() { + // Find distances between vertices. + f_pos = lod_pos(v_pos, focus_pos.xy); + 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; + + // f_pos = lod_pos(focus_pos.xy + splay(v_pos) * /*1000000.0*/(1 << 20), square); + + // f_norm = lod_norm(f_pos.xy); + + // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + + f_pos.z -= 1.0 / pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0); + + // f_pos.z -= 100.0 * pow(1.0 + 0.01 / view_distance.x, -pow(distance(focus_pos.xy, f_pos.xy), 2.0)); + // f_pos.z = mix(-f_pos.z, f_pos.z, view_distance.x <= distance(focus_pos.xy, f_pos.xy) + 32.0); + + // bool faces_fluid = false;// bool((f_pos_norm >> 28) & 0x1u); + // // TODO: Measure real water surface altitude here. + // float surfaceAlt = mix(view_distance.z, /*floor*/(min(f_pos.z, floor(alt_at_real(cam_pos.xy)))), medium.x); + // // float surfaceAlt = mix(view_distance.z, floor(max(cam_pos.z, alt_at_real(cam_pos.xy))), medium.x); + // // float surfaceAlt = min(floor(f_pos.z), floor(alt_at_real(cam_pos.xy))); // faces_fluid ? max(ceil(f_pos.z), floor(f_alt)) : floor(f_alt); + + // f_pos.z -= max(sign(view_distance.x - distance(focus_pos.xy, f_pos.xy)), 0.0) * (32.0 * view_distance.z / 255 + 32.0 * max(0.0, f_pos.z - cam_pos.z)); + // f_pos.z -= 0.1 + max(view_distance.x - distance(focus_pos.xy, f_pos.xy), 0.0) * (1.0 + max(1.0, ceil(f_pos.z - focus_pos.z))); + + // vec3 wRayinitial = f_pos; // cam_pos.z < f_pos.z ? f_pos : cam_pos.xyz; + // vec3 wRayfinal = cam_pos.xyz; // cam_pos.z < f_pos.z ? cam_pos.xyz : f_pos; + // wRayfinal = dot(wRayfinal - wRayinitial, focus_pos.xyz - cam_pos.xyz) < 0.0 ? wRayfinal : wRayinitial; + // vec3 wRayNormal = /*surfaceAlt < wRayinitial.z ? vec3(0.0, 0.0, -1.0) : */vec3(0.0, 0.0, 1.0); + // float n_camera = mix(1.0, 1.3325, medium.x); + // float n_vertex = faces_fluid ? 1.3325 : 1.0; + // float n1 = n_vertex; // cam_pos.z < f_pos.z ? n_vertex : n_camera; + // float n2 = n_camera; // cam_pos.z < f_pos.z ? n_camera : n_vertex; + + // float wRayLength0 = length(wRayfinal - wRayinitial); + // vec3 wRayDir = (wRayfinal - wRayinitial) / wRayLength0; + // vec3 wPoint = wRayfinal; + // bool wIntersectsSurface = IntersectRayPlane(wRayinitial, wRayDir, vec3(0.0, 0.0, surfaceAlt), -wRayNormal, wPoint); + // float wRayLength = length(wPoint - wRayinitial); + // wPoint = wRayLength < wRayLength0 ? wPoint : wRayfinal; + // wRayLength = min(wRayLength, wRayLength0); // min(max_length, dot(wRayfinal - wpos, defaultpos - wpos)); + + // // vec3 wRayDir2 = (wRayfinal - wRayinitial) / wRayLength; + + // vec3 wRayDir3 = (dot(wRayDir, wRayNormal) < 0.0 && surfaceAlt < wRayinitial.z && wIntersectsSurface/* && medium.x == 1u*/) ? refract(wRayDir, wRayNormal, n2 / n1) : wRayDir; + // // wPoint -= wRayDir3 * wRayLength * n2 / n1; + + // vec3 newRay = (dot(wRayDir3, focus_pos.xyz - cam_pos.xyz) < 0.0 && /*dot(wRayDir, wRayNormal) > 0.0 && *//*surfaceAlt < wRayinitial.z && */wIntersectsSurface && medium.x == 1u) ? wPoint - wRayDir3 * wRayLength * n2 / n1/*wPoint - wRayDir3 * wRayLength * n2 / n1*/ : f_pos;// - (wRayfinal - wPoint) * n2 / n1; // wPoint + n2 * (wRayfinal - wPoint) - n2 / n1 * wRayLength * wRayDir3; + + // newRay.z -= max(view_distance.x - distance(focus_pos.xy, f_pos.xy), 0.0) * (1.0 + max(0.0, f_pos.z - focus_pos.z)); + + + // f_light = 1.0; + + gl_Position = + /* 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); +} diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl index ebbc8e1786..4dfbd6dbca 100644 --- a/assets/voxygen/shaders/particle-frag.glsl +++ b/assets/voxygen/shaders/particle-frag.glsl @@ -1,38 +1,86 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #include in vec3 f_pos; flat in vec3 f_norm; in vec3 f_col; -in float f_ao; -in float f_light; out vec4 tgt_color; #include #include +#include const float FADE_DIST = 32.0; void main() { - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - float point_shadow = shadow_at(f_pos, f_norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; - float ao = pow(f_ao, 0.5) * 0.85 + 0.15; - ambient_light *= ao; - diffuse_light *= ao; - vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + vec3 view_dir = -cam_to_frag; +#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); +#elif (SHADOW_MODE == SHADOW_MODE_NONE) + float sun_shade_frac = 1.0; +#endif + float moon_shade_frac = 1.0; + + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac); + + vec3 surf_color = f_col; + float alpha = 1.0; + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // To account for prior saturation. + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + + max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_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(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); + vec3 fog_color = get_sky_color(cam_to_frag, 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, 0.3); } diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 14a597d429..082fc4e453 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -1,11 +1,23 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include #include #include in vec3 v_pos; -in uint v_col; +// in uint v_col; in uint v_norm_ao; in vec3 inst_pos; in float inst_time; @@ -163,7 +175,7 @@ void main() { ); } - f_pos = inst_pos + (v_pos * attr.scale * SCALE + attr.offs); + f_pos = (inst_pos - focus_off.xyz) + (v_pos * attr.scale * SCALE + attr.offs); // 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)); @@ -175,12 +187,8 @@ void main() { f_col = //srgb_to_linear(col) * srgb_to_linear(attr.col); - f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; - - f_light = 1.0; gl_Position = all_mat * vec4(f_pos, 1); - 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 1aba8bc4fa..693bc30409 100644 --- a/assets/voxygen/shaders/player-shadow-frag.glsl +++ b/assets/voxygen/shaders/player-shadow-frag.glsl @@ -1,20 +1,38 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #include 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; 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) @@ -29,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 1a3c12cbbe..cb179bfec3 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -1,8 +1,25 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + #include // Note: The sampler uniform is declared here because it differs for MSAA #include +#include in vec2 f_pos; @@ -29,15 +46,138 @@ vec3 hsv2rgb(vec3 c) { return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } +vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted, vec3 reflected) { + const float NIGHT_EXPOSURE = 10.0; + const float DUSK_EXPOSURE = 2.0;//0.8; + const float DAY_EXPOSURE = 1.0;//0.7; + + const float DAY_SATURATION = 1.0; + const float DUSK_SATURATION = 0.6; + const float NIGHT_SATURATION = 0.1; + + const float gamma = /*0.5*//*1.*0*/1.0;//1.0; + /* float light = length(emitted + reflected); + float color = srgb_to_linear(emitted + reflected); + float avg_col = (color.r + color.g + color.b) / 3.0; + return ((color - avg_col) * light + reflected * avg_col) * (emitted + reflected); */ + // float max_intensity = vec3(1.0); + vec3 color = emitted + reflected; + 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); + // 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)); + float sky_light = lum; + + // 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) + // ); + float alpha = 1.0;//log(1.0 - lum) / lum; + // vec3 now_light = moon_dir.z < 0 ? moon_dir : sun_dir; + // 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); + float alph = sky_light > 0.0 && max_light > 0.0 ? mix(1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light / (0.0 + sky_light)), 1.0, clamp(max_light - sky_light, 0.0, 1.0)) : 1.0; + alpha = alpha * alph;// min(alph, 1.0);//((max_light > 0.0 && max_light > sky_light /* && sky_light > 0.0*/) ? /*1.0*/1.0 / log(/*1.0*//*1.0 + *//*lum_sky + */1.0 + max_light - (0.0 + sky_light)) : 1.0); + // alpha = alpha * min(1.0, (max_light == 0.0 ? 1.0 : (1.0 + abs(lum_sky)) / /*(1.0 + max_light)*/max_light)); + + vec3 col_adjusted = lum == 0.0 ? vec3(0.0) : color / lum; + + // float L = lum == 0.0 ? 0.0 : log(lum); + + + // // float B = T; + // // float B = L + log(alpha); + // float B = lum; + + // float D = L - B; + + // float o = 0.0;//log(PERSISTENT_AMBIANCE); + // float scale = /*-alpha*/-alpha;//1.0; + + // float B_ = (B - o) * scale; + + // // float T = lum; + // float O = exp(B_ + D); + + float T = 1.0 - exp(-alpha * lum);//lum / (1.0 + lum); + // float T = lum; + + // Heuristic desaturation + // const float s = 0.8; + float s = 1.0; + // float s = mix( + // 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)); + // s = max_light / (1.0 + max_light); + + vec3 c = pow(col_adjusted, vec3(s)) * T; + // vec3 c = col_adjusted * T; + // vec3 c = sqrt(col_adjusted) * T; + // vec3 c = /*col_adjusted * */col_adjusted * T; + + return c; + // float sum_col = color.r + color.g + color.b; + // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); +} + 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; + // delta = /*sqrt(2.0) / 2.0 - */sqrt(vec2(dot(delta, delta))); + // delta = 0.5 - vec2(min(delta.x, delta.y)); + delta = vec2(0.25);//vec2(dot(/*0.5 - */delta, /*0.5 - */delta));//vec2(min(delta.x, delta.y));//sqrt(2.0) * (0.5 - vec2(min(delta.x, delta.y))); + // delta = vec2(sqrt(dot(delta, delta))); + // vec2 delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*/1.0 - vec2(sqrt(dot(uv, 1.0 - uv)));//min(uv * (1.0 - uv), 0.25) * 2.0; + // float delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*/1.0 - (dot(uv - 0.5, uv - 0.5));//0.01;//25; + // vec2 delta = /*sqrt*//*sqrt(2.0) / 2.0*//*sqrt(2.0) / 2.0*/sqrt(uv * (1.0 - uv));//min(uv * (1.0 - uv), 0.25) * 2.0; + + // float bright_color0 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(0.0, 0.0), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color1 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(delta.x, delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color2 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(delta.x, -delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color3 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(-delta.x, delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + // float bright_color4 = rel_luminance(texelFetch/*texture*/(src_color, ivec2(clamp(c_uv + vec2(-delta.x, -delta.y), 0.0, 1.0) * screen_res.xy/* / 50*/)/* * 50*/, 0).rgb); + + // float bright_color0 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(0.0, 0.0), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color1 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(delta, delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color2 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(delta, -delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color3 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(-delta, delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + // float bright_color4 = rel_luminance(texture(src_color, /*ivec2*/(clamp(c_uv + vec2(-delta, -delta), 0.0, 1.0)/* * screen_res.xy*//* / 50*/)/* * 50*/, 0).rgb); + + // float bright_color = max(bright_color0, max(bright_color1, max(bright_color2, max(bright_color3, bright_color4))));// / 2.0;// / 5.0; + + // float bright_color = (bright_color0 + bright_color1 + bright_color2 + bright_color3 + bright_color4) / 5.0; vec4 aa_color = aa_apply(src_color, uv * screen_res.xy, screen_res.xy); + // aa_color.rgb = illuminate(1.0 - 1.0 / (1.0 + bright_color), normalize(cam_pos.xyz - focus_pos.xyz), /*vec3 max_light, */vec3(0.0), aa_color.rgb); + //vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a); //hsva_color.y *= 1.45; //hsva_color.z *= 0.85; @@ -46,9 +186,11 @@ void main() { vec4 final_color = pow(aa_color, gamma); +#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 eef08e40d4..d88985486b 100644 --- a/assets/voxygen/shaders/postprocess-vert.glsl +++ b/assets/voxygen/shaders/postprocess-vert.glsl @@ -1,5 +1,21 @@ #version 330 core +#include + +#define LIGHTING_TYPE (LIGHTING_TYPE_TRANSMISSION | LIGHTING_TYPE_REFLECTION) + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + #include in vec2 v_pos; @@ -14,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 d537a1f9eb..a39977036d 100644 --- a/assets/voxygen/shaders/skybox-frag.glsl +++ b/assets/voxygen/shaders/skybox-frag.glsl @@ -1,7 +1,24 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_TRANSMISSION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + #include #include +#include in vec3 f_pos; @@ -13,14 +30,34 @@ 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); + + float cam_alt = alt_at(cam_pos.xy); + // float f_alt = alt_at(f_pos.xy); + float fluid_alt = medium.x == 1u ? floor(cam_alt + 1) : view_distance.w; + // float fluid_alt = max(f_pos.z + 1, floor(f_alt)); + vec3 mu = medium.x == 1u /* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0); + // vec3 sun_attenuation = compute_attenuation(wpos, -sun_dir, mu, surface_alt, wpos); + vec3 cam_attenuation = compute_attenuation(cam_pos.xyz, -cam_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*//*f_pos*//*vec3(f_pos.xy, fluid_alt)*/cam_pos.xyz); + // vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz); + // vec3 cam_attenuation = vec3(1.0); + + + /* vec3 world_pos = cam_pos.xyz + cam_dir * 500000.0; + tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, world_pos, 1.0, true, _clouds), 1.0); */ float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); float dist = 100000.0; - if (medium.x == 1u) { - dist = UNDERWATER_MIST_DIST; - } - vec3 wpos = cam_pos.xyz + normalize(f_pos) * dist; - tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, true, _clouds), 1.0); + float refractionIndex = medium.x == 1u ? 1.0 / 1.3325 : 1.0; + /* if (medium.x == 1u) { + dist = UNDERWATER_MIST_DIST; + } */ + vec3 wpos = cam_pos.xyz + /*normalize(f_pos)*/cam_dir * dist; + + tgt_color = vec4(cam_attenuation * get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, true, refractionIndex, _clouds), 1.0); } diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl index 8cfe045eda..86759a29fe 100644 --- a/assets/voxygen/shaders/skybox-vert.glsl +++ b/assets/voxygen/shaders/skybox-vert.glsl @@ -1,5 +1,21 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_TRANSMISSION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR + +#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 + #include in vec3 v_pos; @@ -12,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 4b983e5ae7..fd97f688ac 100644 --- a/assets/voxygen/shaders/sprite-frag.glsl +++ b/assets/voxygen/shaders/sprite-frag.glsl @@ -1,38 +1,199 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +#define HAS_SHADOW_MAPS + #include in vec3 f_pos; flat in vec3 f_norm; -in vec3 f_col; -in float f_ao; -in float f_light; +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; #include #include +#include const float FADE_DIST = 32.0; void main() { - vec3 light, diffuse_light, ambient_light; - get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); - float point_shadow = shadow_at(f_pos, f_norm); - diffuse_light *= f_light * point_shadow; - ambient_light *= f_light, point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - diffuse_light += point_light; - float ao = pow(f_ao, 0.5) * 0.85 + 0.15; - ambient_light *= ao; - diffuse_light *= ao; - vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); + /* 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); */ + // 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 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). + // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5); + // NOTE: current assumption is that moon and sun shouldn't be out at the sae time. + // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow + // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally). + // float shade_frac = sun_shade_frac + moon_shade_frac; + + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); + float point_shadow = shadow_at(f_pos, f_norm); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*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; + const float n2 = 1.5; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + float R_s = (f_pos.z < f_alt) ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); + + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + vec3 emitted_light, reflected_light; + + // To account for prior saturation. + // float vert_light = pow(f_light, 1.5); + // vec3 light_frac = light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); + /* light_frac += light_reflection_factor(f_norm, view_dir, vec3(1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(-1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, -1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, 1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); */ + + // vec3 light, diffuse_light, ambient_light; + // vec3 emitted_light, reflected_light; + // float point_shadow = shadow_at(f_pos,f_norm); + // vec3 point_light = light_at(f_pos, f_norm); + // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)); + // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + float max_light = 0.0; + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*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; + // ambient_light *= f_light * point_shadow; + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + + max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + + // float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; + 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(normalize(f_pos - cam_pos.xyz), 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 91cc75e6d2..e8c4c33270 100644 --- a/assets/voxygen/shaders/sprite-vert.glsl +++ b/assets/voxygen/shaders/sprite-vert.glsl @@ -1,62 +1,236 @@ #version 330 core +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + #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; -out float f_light; +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 -= 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 *= 8.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 = -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 037357d0af..efbc5d4125 100644 --- a/assets/voxygen/shaders/terrain-frag.glsl +++ b/assets/voxygen/shaders/terrain-frag.glsl @@ -1,69 +1,373 @@ #version 330 core +// #extension GL_ARB_texture_storage : require + +#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 #include #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; -in vec3 f_col; -in float f_light; -in float f_ao; +// #else +// const uint f_pos_norm = 0u; +// #endif +// in float f_alt; +// in vec4 f_shadow; +// 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; #include #include - -float vmax(vec3 v) { - return max(v.x, max(v.y, v.z)); -} +#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 - 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)); + 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))), + // hash(floor(vec4(0, 0, f_pos.z, 2))), + // 1.0 + // ); + // vec3 f_col = light_col.rgb;//vec4(1.0, 0.0, 0.0, 1.0); + // tgt_color = vec4(f_col, 1.0); + // 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 ++) { + // // uint i = 1u; + // Light L = lights[i/* / 6*/]; + + // /* vec4 light_col = vec4( + // hash(vec4(1.0, 0.0, 0.0, i)), + // hash(vec4(1.0, 1.0, 0.0, i)), + // hash(vec4(1.0, 0.0, 1.0, i)), + // 1.0 + // ); */ + // vec3 light_col = vec3(1.0);//L.light_col.rgb; + // float light_strength = L.light_col.a / 255.0; + // // float light_strength = 1.0 / light_shadow_count.x; + + // vec3 light_pos = L.light_pos.xyz; + + // // Pre-calculate difference between light and fragment + // vec3 fragToLight = f_pos - light_pos; + + // // vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u]; + + // // use the light to fragment vector to sample from the depth map + // float bias = 0.0;//0.05;//0.05; + // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, i)/*, 0.0*//*, bias*/).r; + // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, lightIndex), bias); + // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, i + 1)/*, bias*/).r; + // float currentDepth = VectorToDepth(fragToLight) + bias; + // float closestDepth = texture(t_shadow_maps, vec3(fragToLight)/*, -2.5*/).r; + // + // // float visibility = texture(t_shadow_maps, vec4(fragToLight, i + 1), -(length(fragToLight) - bias)/* / screen_res.w*/); + // // it is currently in linear range between [0,1]. Re-transform back to original value + // // closestDepth *= 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; + + // // tgt_color += light_col * vec4(vec3(/*closestDepth*/visibility/* + bias*//* / screen_res.w */) * 1.0 / light_shadow_count.x, 0.0); + // // tgt_color.rgb += light_col * vec3(closestDepth + 0.05 / screen_res.w) * 1.0 /*/ light_shadow_count.x*/ * light_strength; + // tgt_color.rgb += light_col * vec3(closestDepth) * 1.0 / screen_res.w /*/ light_shadow_count.x*/ * light_strength; + // 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; + // 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)); - float ao = pow(f_ao, 0.5) * 0.9 + 0.1; + // /* if (light_shadow_count.x == 1) { + // tgt_color.rgb = vec3(0.0); + // } */ + // if (sum > 0.0) { + // tgt_color.rgb /= sum; + // } + // return; + // Whether this face is facing fluid or not. + bool faces_fluid = bool((f_pos_norm >> 28) & 0x1u); - vec3 light, diffuse_light, ambient_light; - 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 *= point_shadow; - ambient_light *= point_shadow; - vec3 point_light = light_at(f_pos, f_norm); - light += point_light; - ambient_light *= f_light * ao; - diffuse_light *= f_light * ao; - diffuse_light += point_light * ao; + vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); + vec3 view_dir = -cam_to_frag; + // vec3 view_dir = normalize(f_pos - cam_pos.xyz); - vec3 col = f_col + hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0)) * 0.02; // Small-scale noise + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ - // Select glowing - if (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.01)) { - if (vmax(abs(mod(f_pos - f_norm * 0.5, 1.0) - 0.5)) > 0.45) { - col *= 0.5; - } - } +#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 - vec3 surf_color = illuminate(srgb_to_linear(col), light, diffuse_light, ambient_light); + float alpha = 1.0;//0.0001;//1.0; + // TODO: Possibly angle with water surface into account? Since we can basically assume it's horizontal. + const float n2 = 1.5;//1.01; + const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); + const float R_s1s0 = pow((1.3325 - n2) / (1.3325 + n2), 2); + const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2); + const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2); + // float faces_fluid = faces_fluid && f_pos.z <= floor(f_alt); + float fluid_alt = max(f_pos.z + 1, floor(f_alt)); + float R_s = /*(f_pos.z < f_alt)*/faces_fluid /*&& f_pos.z <= fluid_alt*/ ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x); - float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); + // vec3 surf_color = /*srgb_to_linear*/(f_col); + vec3 k_a = vec3(1.0); + vec3 k_d = vec3(1.0); + vec3 k_s = vec3(R_s); + + // 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); */ + // 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); +#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; + + // 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; + + // After shadows are computed, we use a refracted sun and moon direction. + // sun_dir = faces_fluid && sun_shade_frac > 0.0 ? refract(sun_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), 1.0 / 1.3325) : sun_dir; + // moon_dir = faces_fluid && moon_shade_frac > 0.0 ? refract(moon_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), 1.0 / 1.3325) : moon_dir; + + // Compute attenuation due to water from the camera. + vec3 mu = faces_fluid/* && 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. + vec3 cam_attenuation = + medium.x == 1u ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos) + : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz); + + // Computing light attenuation from water. + vec3 emitted_light, reflected_light; + // To account for prior saturation + /*float */f_light = faces_fluid ? 1.0 : f_light * sqrt(f_light); + + emitted_light = vec3(1.0); + reflected_light = vec3(1.0); + float f_select = (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.5)) ? 1.0 / PERSISTENT_AMBIANCE : 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 * f_select/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, f_norm, 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; + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + + // float point_shadow = shadow_at(f_pos, f_norm); + // vec3 point_light = light_at(f_pos, f_norm); + // vec3 light, diffuse_light, ambient_light; + + // get_sun_diffuse(f_norm, time_of_day.x, cam_to_frag, k_a * f_light, k_d * f_light, k_s * f_light, alpha, emitted_light, reflected_light); + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // float point_shadow = shadow_at(f_pos, f_norm); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light * point_shadow; + // vec3 point_light = light_at(f_pos, f_norm); + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; + // reflected_light += light_reflection_factor(norm, cam_to_frag, , vec3 k_d, vec3 k_s, float alpha) { + + // light_reflection_factorplight_reflection_factor + + // vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light); + vec3 f_chunk_pos = f_pos - (model_offs - focus_off.xyz); + float noise = hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0));//0.005/* - 0.01*/; + +//vec3 srgb_to_linear(vec3 srgb) { +// bvec3 cutoff = lessThan(srgb, vec3(0.04045)); +// vec3 higher = pow((srgb + vec3(0.055))/vec3(1.055), vec3(2.4)); +// vec3 lower = srgb/vec3(12.92); +// +// return mix(higher, lower, cutoff); +//} +// +//vec3 linear_to_srgb(vec3 col) { +// // bvec3 cutoff = lessThan(col, vec3(0.0060)); +// // return mix(11.500726 * col, , cutoff); +// vec3 s1 = vec3(sqrt(col.r), sqrt(col.g), sqrt(col.b)); +// vec3 s2 = vec3(sqrt(s1.r), sqrt(s1.g), sqrt(s1.b)); +// vec3 s3 = vec3(sqrt(s2.r), sqrt(s2.g), sqrt(s2.b)); +// return vec3( +// mix(11.500726 * col.r, (0.585122381 * s1.r + 0.783140355 * s2.r - 0.368262736 * s3.r), clamp((col.r - 0.0060) * 10000.0, 0.0, 1.0)), +// mix(11.500726 * col.g, (0.585122381 * s1.g + 0.783140355 * s2.g - 0.368262736 * s3.g), clamp((col.g - 0.0060) * 10000.0, 0.0, 1.0)), +// mix(11.500726 * col.b, (0.585122381 * s1.b + 0.783140355 * s2.b - 0.368262736 * s3.b), clamp((col.b - 0.0060) * 10000.0, 0.0, 1.0)) +// ); +// +// 11.500726 +//} + // vec3 noise_delta = vec3(noise * 0.005); + // vec3 noise_delta = noise * 0.02 * (1.0 - vec3(0.2126, 0.7152, 0.0722)); + // vec3 noise_delta = noise * 0.002 / vec3(0.2126, 0.7152, 0.0722); + // vec3 noise_delta = sqrt(f_col) + noise; + /* vec3 noise_delta = f_col + noise * 0.02; + noise_delta *= noise_delta; + noise_delta -= f_col; */ + // vec3 noise_delta = (1.0 - f_col) * 0.02 * noise * noise; + // + // a = 0.055 + // + // 1 / (1 + a) = 1 / (1 + 0.055) ~ 0.947867299 + // + // l2s = x^(1/2.4) * (1 / (1 + a)) - a + c + // s2l = (l + a)^2.4 * (1 / (1 + a))^2.4 + // = ((x^(1/2.4) * (1 / (1 + a)) - a + c) + a)^2.4 * (1 / (1 + a))^2.4 + // = (x^(1/2.4) * (1 / (1 + a)) + c)^2.4 * (1 / (1 + a))^2.4 + // + // ~ (x^(1/2) * 1 / (1 + a) + c)^2 * (1 / (1 + a))^2 + // + // = ((x + a)^2.4 * (1 / (1 + a))^2.4 + c)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // = (((x + a)^2.4 + c * (1 + a)^2.4) * (1 / (1 + a))^2.4)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // = ((x + a)^2.4 + c * (1 + a)^2.4)^(1/2.4) * ((1 / (1 + a))^2.4)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // = ((x + a)^2.4 + c * (1 + a)^2.4)^(1/2.4) * (1 / (1 + a))^(1/2.4) + // + // = ((x + a)^2 + c * (1 + a)^2)^(1/2) * (1 / (1 + a))^(1/2) + // = (x^2 + a^2 + 2xa + c + ca^2 + 2ac)^(1/2) * (1 / (1 + a))^(1/2) + // + const float A = 0.055; + const float W_INV = 1 / (1 + A); + const float W_2 = W_INV * W_INV;//pow(W_INV, 2.4); + const float NOISE_FACTOR = 0.02;//pow(0.02, 1.2); + vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR); + // noise_delta = noise_delta * noise_delta * W_2 - f_col; + // lum = W ⋅ col + // lum + noise = W ⋅ (col + delta) + // W ⋅ col + noise = W ⋅ col + W ⋅ delta + // noise = W ⋅ delta + // delta = noise / W + // vec3 col = (f_col + noise_delta); + vec3 col = noise_delta * noise_delta * W_2; + // vec3 col = srgb_to_linear(linear_to_srgb(f_col) + noise * 0.02); + // vec3 col = /*srgb_to_linear*/(f_col + noise); // 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); + +#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(normalize(f_pos - cam_pos.xyz), time_of_day.x, cam_pos.xyz, f_pos, 1.0, true, clouds); + 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 0b4664b24d..e3d9615350 100644 --- a/assets/voxygen/shaders/terrain-vert.glsl +++ b/assets/voxygen/shaders/terrain-vert.glsl @@ -1,42 +1,171 @@ #version 330 core +#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 + #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; }; -out vec3 f_pos; -out vec3 f_chunk_pos; -flat out uint f_pos_norm; -out vec3 f_col; -out float f_light; -out float f_ao; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; -const int EXTRA_NEG_Z = 65536; +out vec3 f_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 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() { - f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0x1FFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); - f_pos = f_chunk_pos + model_offs; + // 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)); + 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 -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + // 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_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; + // vec3 light_col = vec3( + // 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_light = float(v_col_light & 0x3Fu) / 64.0; - f_ao = float((v_col_light >> 6u) & 3u) / 4.0; + // 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; + // 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); + // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); + + // IDEA: Cast a ray from the vertex to the camera (if this vertex is above the camera) or from the camera to the vertex (if this + // vertex is below the camera) to see where it intersects the plane of water. All of this only applies if either the terrain + // vertex is in water, or the camera is in water. + // + // If an intersection is found, refract the ray across the barrier using the correct ratio of indices of refraction (1 / N_WATER + // if the vertex is above the camera [ray is going from air to water], N_WATER if the camera is above the vertex + // [ray is going from water to air]). + // + // In order to make sure that terrain and other objects below such an interface are properly renered, we then "un-refract" by + // reversing the refracted vector, and multiplying that by the distance from the object from which we cast the ray to the + // intersectng point, in order to make the object appear to the viewer where it should after refraction. + // bool faces_fluid = bool((f_pos_norm >> 28) & 0x1u); + // // TODO: Measure real water surface altitude here. + // float surfaceAlt = faces_fluid ? max(ceil(f_pos.z), floor(f_alt)) : /*floor(f_alt);*/mix(view_distance.z, min(f_alt, floor(alt_at_real(cam_pos.xy))), medium.x); + + // vec3 wRayinitial = f_pos; // cam_pos.z < f_pos.z ? f_pos : cam_pos.xyz; + // vec3 wRayfinal = cam_pos.xyz; // cam_pos.z < f_pos.z ? cam_pos.xyz : f_pos; + // vec3 wRayNormal = surfaceAlt < wRayinitial.z ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0); + // float n_camera = mix(1.0, 1.3325, medium.x); + // float n_vertex = faces_fluid ? 1.3325 : 1.0; + // float n1 = n_vertex; // cam_pos.z < f_pos.z ? n_vertex : n_camera; + // float n2 = n_camera; // cam_pos.z < f_pos.z ? n_camera : n_vertex; + + // float wRayLength0 = length(wRayfinal - wRayinitial); + // vec3 wRayDir = (wRayfinal - wRayinitial) / wRayLength0; + // vec3 wPoint = wRayfinal; + // bool wIntersectsSurface = IntersectRayPlane(wRayinitial, wRayDir, vec3(0.0, 0.0, surfaceAlt), -wRayNormal, wPoint); + // float wRayLength = length(wPoint - wRayinitial); + // wPoint = wRayLength < wRayLength0 ? wPoint : wRayfinal; + // wRayLength = min(wRayLength, wRayLength0); // min(max_length, dot(wRayfinal - wpos, defaultpos - wpos)); + + // // vec3 wRayDir2 = (wRayfinal - wRayinitial) / wRayLength; + + // vec3 wRayDir3 = (dot(wRayDir, wRayNormal) < 0.0 && wIntersectsSurface) ? refract(wRayDir, wRayNormal, n2 / n1) : wRayDir; + // // 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 * - vec4(f_pos, 1); - gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); + /*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 = -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..0a95e0a591 100644 --- a/assets/voxygen/shaders/ui-vert.glsl +++ b/assets/voxygen/shaders/ui-vert.glsl @@ -22,30 +22,39 @@ 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. + gl_Position = vec4(v_pos, -1.0, 1.0); vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); + // TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3). + vec2 aspect_ratio = textureSize(u_tex, 0).yx; 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); + vec2 v_centered = (v_uv - v_center) / aspect_ratio; + vec2 v_rotated = look_at * v_centered; + f_uv = aspect_ratio * v_rotated + v_center; } else if (v_mode == uint(5)) { // HACK: North facing target rectangle. f_uv = v_uv; - float aspect_ratio = screen_res.x / screen_res.y; vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); + // TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3). + vec2 aspect_ratio = screen_res.yx; 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); + vec2 v_centered = (v_pos - v_center) / aspect_ratio; + vec2 v_rotated = look_at * v_centered; + gl_Position = vec4(aspect_ratio * v_rotated + v_center, -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/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox b/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox index 36a6062875..b982e9968d 100644 --- a/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox +++ b/assets/voxygen/voxel/figure/eyes/general/female_blind-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4ebef30fe578b65cb6821ca082d8a94a4bfb761466e7418c80cf5b3f8c1950c +oid sha256:686aecb4bd5f03a9957b9293439b0ace457c0ee6b39cf59134c177be7ca5dca2 size 1152 diff --git a/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox b/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox index 1fb83d3dd6..596a8b9c5f 100644 --- a/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox +++ b/assets/voxygen/voxel/figure/eyes/general/male_blind-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:322447258c4759eb6ddebc4eb778f5755e0a0a7bf8cbe380da991c56a85ec375 +oid sha256:e247a6519fdd7225b032dbd9c5a9aaa5529644b8dd67c47149e38972fb8f74c3 size 1144 diff --git a/assets/voxygen/voxel/humanoid_color_manifest.ron b/assets/voxygen/voxel/humanoid_color_manifest.ron new file mode 100644 index 0000000000..0ca7742fcb --- /dev/null +++ b/assets/voxygen/voxel/humanoid_color_manifest.ron @@ -0,0 +1,285 @@ +#![enable(unwrap_newtypes)] + +( + // NOTE: You can't change the legnths of these arrays without updating num_hair_colors() in + // common/src/comp/body/humanoid.rs. That's because this is a hack; we should really use enum + // variants for hair colors like we do all the other stuff. Once we fix that, this will no + // longer be something you need to worry about. + hair_colors: ( + Danari: [ + (198, 169, 113), // Philosopher's Grey + //(245, 232, 175), // Cream Blonde + //(228, 208, 147), // Gold Blonde + //(228, 223, 141), // Platinum Blonde + (176, 106, 41), // Summer Blonde + (107, 76, 51), // Oak + //(203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (107, 32, 60), // Grape Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + //(146, 32, 32), // Autumn Red + (20, 19, 17), // Black + ], + Dwarf: [ + (210, 204, 130), // Platinum Blonde + (220, 199, 119), // Cream Blonde + (212, 156, 73), // Gold Blonde + (176, 106, 41), // Summer Blonde + (216, 146, 114), // Matte Pink + (107, 76, 51), // Oak + (203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (191, 228, 254), // Ice NobleBlue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (146, 166, 172), // Matte Green + (0, 139, 58), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + Elf: [ + (66, 83, 113), // Mysterious Blue + (13, 76, 41), // Rainforest Green + (245, 232, 175), // Cream Blonde + (212, 156, 73), // Gold Blonde + (228, 223, 141), // Platinum Blonde + (176, 106, 41), // Summer Blonde + (107, 76, 51), // Oak + (203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (103, 191, 254), // Ice Blue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (80, 156, 211), // Candy Pink + (216, 146, 114), // Matte Pink + (146, 166, 172), // Matte Green + (84, 139, 107), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + Human: [ + (210, 204, 130), // Platinum Blonde + (220, 199, 119), // Cream Blonde + (212, 156, 73), // Gold Blonde + (176, 106, 41), // Summer Blonde + (216, 146, 114), // Matte Pink + (203, 200, 98), // Light + (107, 76, 51), // Oak + (64, 32, 18), // Skin7 + (86, 72, 81), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (114, 137, 211), // Ice Blue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (80, 156, 211), // Candy Pink + (146, 166, 172), // Matte Green + (84, 139, 107), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + Orc: [ + (66, 66, 59), // Wise Grey + //(107, 76, 51), // Oak + //(203, 154, 98), // Light + (64, 32, 18), // Skin7 + (54, 30, 26), // Dark Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (66, 83, 113), // Mysterious Blue + (20, 19, 17), // Black + ], + Undead: [ + //(245, 232, 175), // Cream Blonde + (228, 208, 147), // Gold Blonde + //(228, 223, 141), // Platinum Blonde + (176, 106, 41), // Summer Blonde + (107, 76, 51), // Oak + (203, 154, 98), // Light + (64, 32, 18), // Skin7 + (86, 72, 71), // Ash + (57, 56, 61), // Raven Black + (101, 83, 95), // Matte Purple + (101, 57, 90), // Witch Purple + (111, 54, 117), // Punky Purple + (135, 38, 39), // Dark Red + (88, 26, 29), // Wine Red + (103, 191, 254), // Ice Blue + (92, 80, 144), // Kingfisher Blue + (146, 198, 238), // Lagoon Blue + (66, 66, 59), // Decayed Grey + //(80, 156, 211), // Candy Pink + (216, 146, 114), // Matte Pink + (0, 131, 122), // Rotten Green + (146, 166, 172), // Matte Green + (84, 139, 107), // Grass Green + (48, 61, 52), // Dark Green + (20, 19, 17), // Black + ], + ), + eye_colors_light: ( + VigorousBlack: (71, 59, 49), + NobleBlue: (75, 158, 191), + CuriousGreen: (110, 167, 113), + LoyalBrown: (73, 42, 36), + ViciousRed: (169,0 ,47), + PumpkinOrange: (220, 156, 19), + GhastlyYellow: (221, 225, 31), + MagicPurple: (137, 4, 177), + ToxicGreen: (1, 223, 1), + ExoticPurple: (95, 32, 111), + SulfurYellow: (235, 198, 94), + AmberOrange: (137, 46, 1), + PineGreen: (0, 78, 56), + CornflowerBlue: (18, 66, 90), + ), + eye_colors_dark: ( + VigorousBlack: (32, 32, 32), + NobleBlue: (62, 130, 159), + CuriousGreen: (81, 124, 84), + LoyalBrown: (54, 30, 26), + ViciousRed: (119, 0, 33), + PumpkinOrange: (209, 145, 18), + GhastlyYellow: (205, 212, 29), + MagicPurple: (110, 3, 143), + ToxicGreen: (1, 185, 1), + ExoticPurple: (69, 23, 80), + SulfurYellow: (209, 176, 84), + AmberOrange: (112, 40, 1), + PineGreen: (0, 54, 38), + CornflowerBlue: (13, 47, 64), + ), + eye_white: (255, 255, 255), + skin_colors_plain: ( + Skin1: (228, 183, 160), + Skin2: (226, 181, 158), + Skin3: (223, 179, 157), + Skin4: (221, 177, 155), + Skin5: (218, 176, 154), + Skin6: (216, 174, 152), + Skin7: (213, 172, 151), + Skin8: (211, 170, 149), + Skin9: (198, 159, 140), + Skin10: (180, 144, 127), + Skin11: (163, 130, 114), + Skin12: (135, 103, 90), + Skin13: (120, 92, 80), + Skin14: (105, 80, 70), + Skin15: (90, 69, 60), + Skin16: (75, 57, 50), + Skin17: (60, 46, 40), + Skin18: (45, 34, 30), + Iron: (135, 113, 95), + Steel: (108, 94, 86), + DanariOne: (43, 166, 224), + DanariTwo: (40, 155, 210), + DanariThree: (37, 143, 195), + DanariFour: (34, 132, 181), + ElfOne: (118, 84, 157), + ElfTwo: (99, 114, 161), + // ElfThree: (230, 188, 198), + OrcOne: (61, 130, 42), + OrcTwo: (82, 117, 36), + OrcThree: (71, 94, 42), + OrcFour: (97, 54, 29), + UndeadOne: (178, 178, 178), + UndeadTwo: (162, 157, 150), + UndeadThree: (145, 135, 121), + ), + skin_colors_light: ( + Skin1: (233, 190, 166), + Skin2: (232, 188, 164), + Skin3: (229, 186, 163), + Skin4: (227, 184, 161), + Skin5: (224, 183, 159), + Skin6: (222, 181, 157), + Skin7: (220, 178, 156), + Skin8: (218, 176, 154), + Skin9: (205, 165, 145), + Skin10: (187, 149, 131), + Skin11: (169, 134, 118), + Skin12: (135, 103, 90), + Skin13: (120, 92, 80), + Skin14: (105, 80, 70), + Skin15: (90, 69, 60), + Skin16: (75, 57, 50), + Skin17: (60, 46, 40), + Skin18: (45, 34, 30), + Iron: (144, 125, 106), + Steel: (120, 107, 99), + DanariOne: (44, 172, 230), + DanariTwo: (41, 161, 217), + DanariThree: (38, 148, 202), + DanariFour: (35, 136, 188), + ElfOne: (122, 87, 163), + ElfTwo: (102, 118, 167), + //ElfThree: (242, 199, 209), + OrcOne: (83, 165, 56), + OrcTwo: (92, 132, 46), + OrcThree: (84, 110, 54), + OrcFour: (97, 54, 29), + UndeadOne: (185, 185, 185), + UndeadTwo: (168, 163, 155), + UndeadThree: (150, 139, 125), + ), + skin_colors_dark: ( + Skin1: (222, 176, 154), + Skin2: (220, 174, 153), + Skin3: (217, 172, 152), + Skin4: (214, 171, 150), + Skin5: (211, 170, 149), + Skin6: (209, 168, 147), + Skin7: (206, 166, 146), + Skin8: (204, 164, 144), + Skin9: (191, 154, 136), + Skin10: (173, 139, 123), + Skin11: (157, 126, 110), + Skin12: (132, 103, 82), + Skin13: (107, 82, 72), + Skin14: (92, 70, 62), + Skin15: (77, 59, 51), + Skin16: (61, 47, 41), + Skin17: (48, 37, 32), + Skin18: (33, 25, 22), + Iron: (124, 99, 82), + Steel: (96, 81, 72), + DanariOne: (43, 166, 224), + DanariTwo: (40, 155, 210), + DanariThree: (37, 143, 195), + DanariFour: (34, 132, 181), + ElfOne: (114, 81, 152), + ElfTwo: (96, 110, 155), + //ElfThree: (217, 178, 187), + OrcOne: (55, 114, 36), + OrcTwo: (70, 104, 29), + OrcThree: (60, 83, 32), + OrcFour: (84, 47, 25), + UndeadOne: (172, 172, 172), + UndeadTwo: (156, 152, 145), + UndeadThree: (128, 119, 107), + ), +) diff --git a/assets/voxygen/voxel/sprite/pumpkin/1.vox b/assets/voxygen/voxel/sprite/pumpkin/1.vox index e04c596c6d..862306e4e3 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/1.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0580c2e316710f88f1bb7a20462e64ad44aa6a47b3b58069d1d6b1015bdec06 +oid sha256:049cec7bd63a666ee7ce53cb4b515890204be79368b81726622e03f5e5d85e9e size 2568 diff --git a/assets/voxygen/voxel/sprite/pumpkin/2.vox b/assets/voxygen/voxel/sprite/pumpkin/2.vox index 2cb8808644..80c74e5046 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/2.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/2.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2616a9c1e18ec29006d0fce787957cb9c19aab3a25b2b360f0dd490515ad57d3 +oid sha256:956f9160ccf98ef2a07f9840ec0d74727179181cb47ce2c9bf28751c21624664 size 2612 diff --git a/assets/voxygen/voxel/sprite/pumpkin/3.vox b/assets/voxygen/voxel/sprite/pumpkin/3.vox index ce49f8bbed..69bf0537ea 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/3.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/3.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f338dd3b94bb36a7f36e5272e847e83bfa9317daf3f7b8535e73ea39842ef1b6 +oid sha256:730f8184875a8589d450bdebb875c874dc57423b42ebc0ab1440f99117885510 size 2580 diff --git a/assets/voxygen/voxel/sprite/pumpkin/4.vox b/assets/voxygen/voxel/sprite/pumpkin/4.vox index ebe4c384a3..cd625d2969 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/4.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/4.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e65e3d6c3bda693b7ef21a86aac13f7a4fa87a09a1b731e58b13369e75751d7 +oid sha256:a81c85e279dd1f291a20261a4043bf4b533c99ea801851052855daf707651fdb size 2592 diff --git a/assets/voxygen/voxel/sprite/pumpkin/5.vox b/assets/voxygen/voxel/sprite/pumpkin/5.vox index 1ebaedd451..a05f24b96d 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/5.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/5.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d96e0c99bd97e8a16d74f6419d6c5ea4740b5fbf82f7d61835f68dfb75debc87 +oid sha256:9803e6c26173ad1b72e269752041f6e14076bbefaa7686d162d10fe57c58cf47 size 2616 diff --git a/assets/voxygen/voxel/sprite/pumpkin/6.vox b/assets/voxygen/voxel/sprite/pumpkin/6.vox index 6e2e3bf480..e35f75668b 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/6.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/6.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0abc7fd43fbb5cfbe6ff4a341a94d40f09693164a0d35c480f297ff1bae5eae +oid sha256:1415d7c7594b9e772a0ccedc25996e24dd3eadb599e9727e713258c80811f67f size 4668 diff --git a/assets/voxygen/voxel/sprite/pumpkin/7.vox b/assets/voxygen/voxel/sprite/pumpkin/7.vox index 3842695cce..d750d82d6d 100644 --- a/assets/voxygen/voxel/sprite/pumpkin/7.vox +++ b/assets/voxygen/voxel/sprite/pumpkin/7.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f965826e3c5d890d32cdc1de331ae60e6d35e672cc6f44a4dd407268f9e66ab +oid sha256:ec16bd5be9598f7b085b01737711877720560f15780afadff20da1fb6b129647 size 4740 diff --git a/assets/world/style/colors.ron b/assets/world/style/colors.ron new file mode 100644 index 0000000000..bc03c21078 --- /dev/null +++ b/assets/world/style/colors.ron @@ -0,0 +1,152 @@ +#![enable(unwrap_newtypes)] +#![enable(implicit_some)] + +// NOTE: Many of these colors are not used directly, but are modified in various ways (e.g. via +// lerping). So don't be too frustrated if a color change seems to have a different effect in +// different places; just follow the trends. +( + block: ( + pyramid: (203, 170, 146), + + // These are all ranges from low to high. + structure_blocks: ( + None: None, + // Samples the surface color. + Grass: None, + // Water blocks ignore color information, and even if they didn't would not be lerped. + Water: None, + GreenSludge: None, + // Leaves all actually get interpolated. + TemperateLeaves: (start: (0, 132, 94), end: (142, 181, 0)), + PineLeaves: (start: (0, 60, 50), end: (30, 100, 10)), + PalmLeavesInner: (start: (61, 166, 43), end: (29, 130, 32)), + PalmLeavesOuter: (start: (62, 171, 38), end: (45, 171, 65)), + Acacia: (start: (15, 126, 50), end: (30, 180, 10)), + Liana: (start: (0, 125, 107), end: (0, 155, 129)), + Mangrove: (start: (32, 56, 22), end: (57, 69, 27)), + ) + + // Water blocks ignore color now so this isn't used, but just in case this color was worth + // remembering here it is. + // green_sludge: (30.0, 126.0, 23.0) + ), + column: ( + cold_grass: (0.0, 0.5, 0.25), + warm_grass: (0.4, 0.8, 0.0), + dark_grass: (0.15, 0.4, 0.1), + wet_grass: (0.1, 0.8, 0.2), + cold_stone: (0.57, 0.67, 0.8), + hot_stone: (0.07, 0.07, 0.06), + warm_stone: (0.77, 0.77, 0.64), + beach_sand: (0.8, 0.75, 0.5), + desert_sand: (0.7, 0.4, 0.25), + snow: (0.8, 0.85, 1.0), + + stone_col: (195, 187, 201), + + dirt_low: (0.075, 0.07, 0.3), + dirt_high: (0.75, 0.55, 0.1), + + snow_high: (0.01, 0.3, 0.0), + warm_stone_high: (0.3, 0.12, 0.2), + + grass_high: (0.15, 0.2, 0.15), + tropical_high: (0.87, 0.62, 0.56), + ), + // NOTE: I think (but am not sure) that this is the color of stuff below the bottom-most + // ground. I'm not sure how easy it is to see. + deep_stone_color: (125, 120, 130), + layer: ( + bridge: (80, 80, 100), + stalagtite: (200, 200, 200), + ), + site: ( + castle: (), + dungeon: ( + stone: (150, 150, 175), + ), + settlement: ( + building: ( + archetype: ( + keep: ( + brick_base: (80, 80, 80), + floor_base: (80, 60, 10), + pole: (90, 70, 50), + flag: ( + Evil: (80, 10, 130), + Good: (200, 80, 40), + ), + stone: ( + Evil: (65, 60, 55), + Good: (100, 100, 110), + ), + ), + house: ( + foundation: (100, 100, 100), + floor: (100, 75, 50), + roof: ( + Roof1: (0x99, 0x5E, 0x54), + Roof2: (0x43, 0x63, 0x64), + Roof3: (0x76, 0x6D, 0x68), + Roof4: (0x7B, 0x41, 0x61), + Roof5: (0x52, 0x20, 0x20), + Roof6: (0x1A, 0x4A, 0x59), + Roof7: (0xCC, 0x76, 0x4E), + // (0x1D, 0x4D, 0x45), + // (0xB3, 0x7D, 0x60), + // (0xAC, 0x5D, 0x26), + // (0x32, 0x46, 0x6B), + // (0x2B, 0x19, 0x0F), + // (0x93, 0x78, 0x51), + // (0x92, 0x57, 0x24), + // (0x4A, 0x4E, 0x4E), + // (0x2F, 0x32, 0x47), + // (0x8F, 0x35, 0x43), + // (0x6D, 0x1E, 0x3A), + // (0x6D, 0xA7, 0x80), + // (0x4F, 0xA0, 0x95), + // (0xE2, 0xB9, 0x99), + // (0x7A, 0x30, 0x22), + // (0x4A, 0x06, 0x08), + // (0x8E, 0xB4, 0x57), + ), + wall: ( + Wall1: (200, 180, 150), + Wall2: (0xB8, 0xB4, 0xA4), + Wall3: (0x76, 0x6D, 0x68), + Wall4: (0xF3, 0xC9, 0x8F), + Wall5: (0xD3, 0xB7, 0x99), + Wall6: (0xE1, 0xAB, 0x91), + Wall7: (0x82, 0x57, 0x4C), + Wall8: (0xB9, 0x96, 0x77), + Wall9: (0xAE, 0x8D, 0x9C), + ), + support: ( + Support1: (60, 45, 30), + Support2: (0x65, 0x55, 0x56), + Support3: (0x53, 0x33, 0x13), + Support4: (0x58, 0x42, 0x33), + ), + ), + ), + ), + plot_town_path: (100, 95, 65), + + plot_field_dirt: (80, 55, 35), + plot_field_mound: (70, 80, 30), + + wall_low: (130, 100, 0), + wall_high :(90, 70, 50), + + tower_color: (50, 50, 50), + + // NOTE: Ideally these would be part of a make_case_elim, but we can't use it beacuse + // it doesn't support struct variants yet. + plot_dirt: (90, 70, 50), + plot_grass: (100, 200, 0), + plot_water: (100, 150, 250), + plot_town: (150, 110, 60), + // TODO: Add field furrow stuff. + ), + ), +) diff --git a/assets/world/tree/acacia/1.vox b/assets/world/tree/acacia/1.vox index 310198c54f..1e34877f5c 100644 --- a/assets/world/tree/acacia/1.vox +++ b/assets/world/tree/acacia/1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e1fee1ea15e891aa424ea8054f5ea5bb02f1e6991c771d4191f0e655aba109b -size 22600 +oid sha256:e1b58989ac01175fba006ec70815959a8cdc7fe3035c43f3464a06afd5f65f98 +size 8360 diff --git a/assets/world/tree/acacia/2.vox b/assets/world/tree/acacia/2.vox index 08cb8d7e49..b7719a4cec 100644 --- a/assets/world/tree/acacia/2.vox +++ b/assets/world/tree/acacia/2.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:976e990871ab219ca3023eaaad8d7e92a5ed4823555b819dabce6c60581a922f -size 2160 +oid sha256:5b17758334983bbd97eef5a66b64c1828bee6df480b745ed2e47e046c047f10e +size 1420 diff --git a/assets/world/tree/acacia/3.vox b/assets/world/tree/acacia/3.vox index cb06009f12..d0c14bc134 100644 --- a/assets/world/tree/acacia/3.vox +++ b/assets/world/tree/acacia/3.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d9bb5949defcad8260a0293a6e42429440dfff4833edfa9a04caf06ef5895e3 -size 3068 +oid sha256:adc156f886d8fddc6c456ec85c21c0561aee7fa5f51a1a9b074b704e3c0b92ad +size 1688 diff --git a/assets/world/tree/acacia/4.vox b/assets/world/tree/acacia/4.vox index 30b2e0efde..ebeb422216 100644 --- a/assets/world/tree/acacia/4.vox +++ b/assets/world/tree/acacia/4.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2071db98db659494ee497e7ed728c3cbf838fb7f02594b5255417e5e13e182ac -size 17712 +oid sha256:ece352f8ce6034fd20efca429b46426a66a7a2e637a6ec82b8e718af14ceb547 +size 8088 diff --git a/assets/world/tree/acacia/5.vox b/assets/world/tree/acacia/5.vox index 9cf8015236..77c24923f8 100644 --- a/assets/world/tree/acacia/5.vox +++ b/assets/world/tree/acacia/5.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fc04f31dd7fb85219500d6145233663e6f1a040aed8b4acc84585e626c10b82 -size 33720 +oid sha256:c030cffb4f8f5f908a70046bef904daa4e065d78f718ec22579a05139fba4103 +size 8564 diff --git a/assets/world/tree/acacia_3/1.vox b/assets/world/tree/acacia_3/1.vox new file mode 100644 index 0000000000..310198c54f --- /dev/null +++ b/assets/world/tree/acacia_3/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e1fee1ea15e891aa424ea8054f5ea5bb02f1e6991c771d4191f0e655aba109b +size 22600 diff --git a/assets/world/tree/acacia_3/2.vox b/assets/world/tree/acacia_3/2.vox new file mode 100644 index 0000000000..08cb8d7e49 --- /dev/null +++ b/assets/world/tree/acacia_3/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:976e990871ab219ca3023eaaad8d7e92a5ed4823555b819dabce6c60581a922f +size 2160 diff --git a/assets/world/tree/acacia_3/3.vox b/assets/world/tree/acacia_3/3.vox new file mode 100644 index 0000000000..cb06009f12 --- /dev/null +++ b/assets/world/tree/acacia_3/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d9bb5949defcad8260a0293a6e42429440dfff4833edfa9a04caf06ef5895e3 +size 3068 diff --git a/assets/world/tree/acacia_3/4.vox b/assets/world/tree/acacia_3/4.vox new file mode 100644 index 0000000000..30b2e0efde --- /dev/null +++ b/assets/world/tree/acacia_3/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2071db98db659494ee497e7ed728c3cbf838fb7f02594b5255417e5e13e182ac +size 17712 diff --git a/assets/world/tree/acacia_3/5.vox b/assets/world/tree/acacia_3/5.vox new file mode 100644 index 0000000000..9cf8015236 --- /dev/null +++ b/assets/world/tree/acacia_3/5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fc04f31dd7fb85219500d6145233663e6f1a040aed8b4acc84585e626c10b82 +size 33720 diff --git a/assets/world/tree/acacia_savannah/1.vox b/assets/world/tree/acacia_savannah/1.vox deleted file mode 100644 index 1e34877f5c..0000000000 --- a/assets/world/tree/acacia_savannah/1.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e1b58989ac01175fba006ec70815959a8cdc7fe3035c43f3464a06afd5f65f98 -size 8360 diff --git a/assets/world/tree/acacia_savannah/2.vox b/assets/world/tree/acacia_savannah/2.vox deleted file mode 100644 index b7719a4cec..0000000000 --- a/assets/world/tree/acacia_savannah/2.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5b17758334983bbd97eef5a66b64c1828bee6df480b745ed2e47e046c047f10e -size 1420 diff --git a/assets/world/tree/acacia_savannah/3.vox b/assets/world/tree/acacia_savannah/3.vox deleted file mode 100644 index d0c14bc134..0000000000 --- a/assets/world/tree/acacia_savannah/3.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:adc156f886d8fddc6c456ec85c21c0561aee7fa5f51a1a9b074b704e3c0b92ad -size 1688 diff --git a/assets/world/tree/acacia_savannah/4.vox b/assets/world/tree/acacia_savannah/4.vox deleted file mode 100644 index ebeb422216..0000000000 --- a/assets/world/tree/acacia_savannah/4.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ece352f8ce6034fd20efca429b46426a66a7a2e637a6ec82b8e718af14ceb547 -size 8088 diff --git a/assets/world/tree/acacia_savannah/5.vox b/assets/world/tree/acacia_savannah/5.vox deleted file mode 100644 index 77c24923f8..0000000000 --- a/assets/world/tree/acacia_savannah/5.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c030cffb4f8f5f908a70046bef904daa4e065d78f718ec22579a05139fba4103 -size 8564 diff --git a/client/Cargo.toml b/client/Cargo.toml index a78c32154e..70733d28dd 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -13,10 +13,12 @@ uvth = "3.1.1" futures-util = "0.3" futures-executor = "0.3" futures-timer = "2.0" -image = { version = "0.22.5", default-features = false, features = ["png"] } +image = { version = "0.23.8", default-features = false, features = ["png"] } +num = "0.2.0" num_cpus = "1.10.1" tracing = { version = "0.1", default-features = false } +rayon = "^1.3.0" specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5" } -vek = { version = "0.11.0", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] } authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" } diff --git a/client/src/lib.rs b/client/src/lib.rs index fe71cde3e5..1c5e28bf6c 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -29,7 +29,7 @@ use common::{ recipe::RecipeBook, state::State, sync::{Uid, UidAllocator, WorldSyncExt}, - terrain::{block::Block, TerrainChunk, TerrainChunkSize}, + terrain::{block::Block, neighbors, TerrainChunk, TerrainChunkSize}, vol::RectVolSize, }; use futures_executor::block_on; @@ -40,6 +40,8 @@ use image::DynamicImage; use network::{ Network, Participant, Pid, ProtocolAddr, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED, }; +use num::traits::FloatConst; +use rayon::prelude::*; use std::{ collections::VecDeque, net::SocketAddr, @@ -74,7 +76,28 @@ pub struct Client { client_state: ClientState, thread_pool: ThreadPool, pub server_info: ServerInfo, - pub world_map: (Arc, Vec2), + /// Just the "base" layer for LOD; currently includes colors and nothing + /// else. In the future we'll add more layers, like shadows, rivers, and + /// probably foliage, cities, roads, and other structures. + pub lod_base: Vec, + /// The "height" layer for LOD; currently includes only land altitudes, but + /// in the future should also water depth, and probably other + /// information as well. + pub lod_alt: Vec, + /// The "shadow" layer for LOD. Includes east and west horizon angles and + /// an approximate max occluder height, which we use to try to + /// approximate soft and volumetric shadows. + pub lod_horizon: Vec, + /// A fully rendered map image for use with the map and minimap; note that + /// this can be constructed dynamically by combining the layers of world + /// map data (e.g. with shadow map data or river data), but at present + /// we opt not to do this. + /// + /// The second element of the tuple is the world size (as a 2D grid, + /// in chunks), and the third element holds the minimum height for any land + /// chunk (i.e. the sea level) in its x coordinate, and the maximum land + /// height above this height (i.e. the max height) in its y coordinate. + pub world_map: (Arc, Vec2, Vec2), pub player_list: HashMap, pub character_list: CharacterList, pub active_character_id: Option, @@ -130,84 +153,217 @@ impl Client { // We reduce the thread count by 1 to keep rendering smooth thread_pool.set_num_threads((num_cpus::get() - 1).max(1)); - let (network, f) = Network::new(Pid::new()); - thread_pool.execute(f); + let (network, scheduler) = Network::new(Pid::new()); + thread_pool.execute(scheduler); let participant = block_on(network.connect(ProtocolAddr::Tcp(addr.into())))?; let mut stream = block_on(participant.open(10, PROMISES_ORDERED | PROMISES_CONSISTENCY))?; // Wait for initial sync - let (state, entity, server_info, world_map, recipe_book, max_group_size) = block_on( - async { - loop { - match stream.recv().await? { - ServerMsg::InitialSync { - entity_package, - server_info, - time_of_day, - max_group_size, - world_map: (map_size, world_map), - recipe_book, - } => { - // TODO: Display that versions don't match in Voxygen - if server_info.git_hash != *common::util::GIT_HASH { - warn!( - "Server is running {}[{}], you are running {}[{}], versions \ - might be incompatible!", - server_info.git_hash, - server_info.git_date, - common::util::GIT_HASH.to_string(), - common::util::GIT_DATE.to_string(), + let ( + state, + entity, + server_info, + lod_base, + lod_alt, + lod_horizon, + world_map, + recipe_book, + max_group_size, + ) = block_on(async { + loop { + match stream.recv().await? { + ServerMsg::InitialSync { + entity_package, + server_info, + time_of_day, + max_group_size, + world_map, + recipe_book, + } => { + // TODO: Display that versions don't match in Voxygen + if server_info.git_hash != *common::util::GIT_HASH { + warn!( + "Server is running {}[{}], you are running {}[{}], versions might \ + be incompatible!", + server_info.git_hash, + server_info.git_date, + common::util::GIT_HASH.to_string(), + common::util::GIT_DATE.to_string(), + ); + } + + debug!("Auth Server: {:?}", server_info.auth_provider); + + // Initialize `State` + let mut state = State::default(); + // Client-only components + state + .ecs_mut() + .register::>(); + + let entity = state.ecs_mut().apply_entity_package(entity_package); + *state.ecs_mut().write_resource() = time_of_day; + + let map_size_lg = common::terrain::MapSizeLg::new(world_map.dimensions_lg) + .map_err(|_| { + Error::Other(format!( + "Server sent bad world map dimensions: {:?}", + world_map.dimensions_lg, + )) + })?; + let map_size = map_size_lg.chunks(); + let max_height = world_map.max_height; + let sea_level = world_map.sea_level; + let rgba = world_map.rgba; + let alt = world_map.alt; + let expected_size = + (u32::from(map_size.x) * u32::from(map_size.y)) as usize; + if rgba.len() != expected_size { + return Err(Error::Other("Server sent a bad world map image".into())); + } + if alt.len() != expected_size { + return Err(Error::Other("Server sent a bad altitude map.".into())); + } + let [west, east] = world_map.horizons; + let scale_angle = + |a: u8| (a as f32 / 255.0 * ::FRAC_PI_2()).tan(); + let scale_height = |h: u8| h as f32 / 255.0 * max_height; + let scale_height_big = |h: u32| (h >> 3) as f32 / 8191.0 * max_height; + + debug!("Preparing image..."); + let unzip_horizons = |(angles, heights): &(Vec<_>, Vec<_>)| { + ( + angles.iter().copied().map(scale_angle).collect::>(), + heights + .iter() + .copied() + .map(scale_height) + .collect::>(), + ) + }; + let horizons = [unzip_horizons(&west), unzip_horizons(&east)]; + + // Redraw map (with shadows this time). + let mut world_map = vec![0u32; rgba.len()]; + let mut map_config = common::terrain::map::MapConfig::orthographic( + map_size_lg, + core::ops::RangeInclusive::new(0.0, max_height), + ); + map_config.horizons = Some(&horizons); + let rescale_height = |h: f32| h / max_height; + let bounds_check = |pos: Vec2| { + pos.reduce_partial_min() >= 0 + && pos.x < map_size.x as i32 + && pos.y < map_size.y as i32 + }; + map_config.generate( + |pos| { + let (rgba, alt, downhill_wpos) = if bounds_check(pos) { + let posi = + pos.y as usize * map_size.x as usize + pos.x as usize; + let [r, g, b, a] = rgba[posi].to_le_bytes(); + let alti = alt[posi]; + // Compute downhill. + let downhill = { + let mut best = -1; + let mut besth = alti; + for nposi in neighbors(map_size_lg, posi) { + let nbh = alt[nposi]; + if nbh < besth { + besth = nbh; + best = nposi as isize; + } + } + best + }; + let downhill_wpos = if downhill < 0 { + None + } else { + Some( + Vec2::new( + (downhill as usize % map_size.x as usize) as i32, + (downhill as usize / map_size.x as usize) as i32, + ) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ) + }; + (Rgba::new(r, g, b, a), alti, downhill_wpos) + } else { + (Rgba::zero(), 0, None) + }; + let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let downhill_wpos = downhill_wpos.unwrap_or( + wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32), ); - } - - debug!("Auth Server: {:?}", server_info.auth_provider); - - // Initialize `State` - let mut state = State::default(); - // Client-only components - state - .ecs_mut() - .register::>(); - - let entity = state.ecs_mut().apply_entity_package(entity_package); - *state.ecs_mut().write_resource() = time_of_day; - - assert_eq!(world_map.len(), (map_size.x * map_size.y) as usize); - let mut world_map_raw = - vec![0u8; 4 * world_map.len()/*map_size.x * map_size.y*/]; - LittleEndian::write_u32_into(&world_map, &mut world_map_raw); - debug!("Preparing image..."); - let world_map = Arc::new( + let alt = rescale_height(scale_height_big(alt)); + common::terrain::map::MapSample { + rgb: Rgb::from(rgba), + alt: f64::from(alt), + downhill_wpos, + connections: None, + } + }, + |wpos| { + let pos = + wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + rescale_height(if bounds_check(pos) { + let posi = + pos.y as usize * map_size.x as usize + pos.x as usize; + scale_height_big(alt[posi]) + } else { + 0.0 + }) + }, + |pos, (r, g, b, a)| { + world_map[pos.y * map_size.x as usize + pos.x] = + u32::from_le_bytes([r, g, b, a]); + }, + ); + let make_raw = |rgba| -> Result<_, Error> { + let mut raw = vec![0u8; 4 * world_map.len()]; + LittleEndian::write_u32_into(rgba, &mut raw); + Ok(Arc::new( image::DynamicImage::ImageRgba8({ // Should not fail if the dimensions are correct. - let world_map = - image::ImageBuffer::from_raw(map_size.x, map_size.y, world_map_raw); - world_map.ok_or_else(|| Error::Other("Server sent a bad world map image".into()))? + let map = + image::ImageBuffer::from_raw(u32::from(map_size.x), u32::from(map_size.y), raw); + map.ok_or_else(|| Error::Other("Server sent a bad world map image".into()))? }) - // Flip the image, since Voxygen uses an orientation where rotation from - // positive x axis to positive y axis is counterclockwise around the z axis. - .flipv(), - ); - debug!("Done preparing image..."); + // Flip the image, since Voxygen uses an orientation where rotation from + // positive x axis to positive y axis is counterclockwise around the z axis. + .flipv(), + )) + }; + let lod_base = rgba; + let lod_alt = alt; + let world_map = make_raw(&world_map)?; + let horizons = (west.0, west.1, east.0, east.1) + .into_par_iter() + .map(|(wa, wh, ea, eh)| u32::from_le_bytes([wa, wh, ea, eh])) + .collect::>(); + let lod_horizon = horizons; + let map_bounds = Vec2::new(sea_level, max_height); + debug!("Done preparing image..."); - break Ok(( - state, - entity, - server_info, - (world_map, map_size), - recipe_book, - max_group_size, - )); - }, - ServerMsg::TooManyPlayers => break Err(Error::TooManyPlayers), - err => { - warn!("whoops, server mad {:?}, ignoring", err); - }, - } + break Ok(( + state, + entity, + server_info, + lod_base, + lod_alt, + lod_horizon, + (world_map, map_size, map_bounds), + recipe_book, + max_group_size, + )); + }, + ServerMsg::TooManyPlayers => break Err(Error::TooManyPlayers), + err => { + warn!("whoops, server mad {:?}, ignoring", err); + }, } - }, - )?; + } + })?; stream.send(ClientMsg::Ping)?; @@ -222,6 +378,9 @@ impl Client { thread_pool, server_info, world_map, + lod_base, + lod_alt, + lod_horizon, player_list: HashMap::new(), character_list: CharacterList::default(), active_character_id: None, diff --git a/common/Cargo.toml b/common/Cargo.toml index 9dd2828065..d9467ee3dc 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -11,12 +11,14 @@ no-assets = [] arraygen = "0.1.13" specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" } +roots = "0.0.6" specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" } -vek = { version = "0.11.0", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } dot_vox = "4.0" -image = { version = "0.22.5", default-features = false, features = ["png"] } +image = { version = "0.23.8", default-features = false, features = ["png"] } serde = { version = "1.0.110", features = ["derive"] } serde_json = "1.0.50" +serde_repr = "0.1.6" ron = { version = "0.6", default-features = false } tracing = { version = "0.1", default-features = false } rand = "0.7" diff --git a/common/src/assets/watch.rs b/common/src/assets/watch.rs index f425c106f1..c5e8739f85 100644 --- a/common/src/assets/watch.rs +++ b/common/src/assets/watch.rs @@ -170,5 +170,13 @@ impl ReloadIndicator { } // Returns true if the watched file was changed - pub fn reloaded(&self) -> bool { self.reloaded.swap(false, Ordering::Acquire) } + pub fn reloaded(&self) -> bool { + // Optimize for the common case by performing an initial relaxed read, avoiding + // the atomic write. + if self.reloaded.load(Ordering::Relaxed) { + self.reloaded.swap(false, Ordering::Acquire) + } else { + false + } + } } diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index f0458c4b17..6661bcc766 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -14,6 +14,7 @@ pub mod quadruped_small; use crate::{ assets::{self, Asset}, + make_case_elim, npc::NpcKind, }; use serde::{Deserialize, Serialize}; @@ -21,23 +22,26 @@ use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; use std::{fs::File, io::BufReader}; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Body { - Humanoid(humanoid::Body) = 0, - QuadrupedSmall(quadruped_small::Body) = 1, - QuadrupedMedium(quadruped_medium::Body) = 2, - BirdMedium(bird_medium::Body) = 3, - FishMedium(fish_medium::Body) = 4, - Dragon(dragon::Body) = 5, - BirdSmall(bird_small::Body) = 6, - FishSmall(fish_small::Body) = 7, - BipedLarge(biped_large::Body) = 8, - Object(object::Body) = 9, - Golem(golem::Body) = 10, - Critter(critter::Body) = 11, - QuadrupedLow(quadruped_low::Body) = 12, -} +make_case_elim!( + body, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Body { + Humanoid(body: humanoid::Body) = 0, + QuadrupedSmall(body: quadruped_small::Body) = 1, + QuadrupedMedium(body: quadruped_medium::Body) = 2, + BirdMedium(body: bird_medium::Body) = 3, + FishMedium(body: fish_medium::Body) = 4, + Dragon(body: dragon::Body) = 5, + BirdSmall(body: bird_small::Body) = 6, + FishSmall(body: fish_small::Body) = 7, + BipedLarge(body: biped_large::Body)= 8, + Object(body: object::Body) = 9, + Golem(body: golem::Body) = 10, + Critter(body: critter::Body) = 11, + QuadrupedLow(body: quadruped_low::Body) = 12, + } +); /// Data representing data generic to the body together with per-species data. /// @@ -60,10 +64,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>, pub quadruped_low: BodyData>, } @@ -87,6 +95,30 @@ 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, + Body::QuadrupedLow(_) => &self.quadruped_low.body, + } + } +} + impl< BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>, SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>, diff --git a/common/src/comp/body/humanoid.rs b/common/src/comp/body/humanoid.rs index ff1276da72..59873b44f7 100644 --- a/common/src/comp/body/humanoid.rs +++ b/common/src/comp/body/humanoid.rs @@ -1,6 +1,7 @@ +use crate::make_case_elim; use rand::{seq::SliceRandom, thread_rng, Rng}; use serde::{Deserialize, Serialize}; -use vek::Rgb; +use serde_repr::{Deserialize_repr, Serialize_repr}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Body { @@ -58,16 +59,19 @@ impl From for super::Body { fn from(body: Body) -> Self { super::Body::Humanoid(body) } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Species { - Danari = 0, - Dwarf = 1, - Elf = 2, - Human = 3, - Orc = 4, - Undead = 5, -} +make_case_elim!( + species, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum Species { + Danari = 0, + Dwarf = 1, + Elf = 2, + Human = 3, + Orc = 4, + Undead = 5, + } +); /// Data representing per-species generic data. /// @@ -114,142 +118,6 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() } } -// Hair Colors -pub const DANARI_HAIR_COLORS: [(u8, u8, u8); 12] = [ - (198, 169, 113), // Philosopher's Grey - //(245, 232, 175), // Cream Blonde - //(228, 208, 147), // Gold Blonde - //(228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - //(203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (107, 32, 60), // Grape Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - //(146, 32, 32), // Autumn Red - (20, 19, 17), // Black -]; -pub const DWARF_HAIR_COLORS: [(u8, u8, u8); 21] = [ - (245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - (228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice NobleBlue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (174, 148, 161), // Matte Pink - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; -pub const ELF_HAIR_COLORS: [(u8, u8, u8); 24] = [ - (66, 83, 113), // Mysterious Blue - (13, 76, 41), // Rainforest Green - (245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - (228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice Blue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (224, 182, 184), // Candy Pink - (174, 148, 161), // Matte Pink - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; -pub const HUMAN_HAIR_COLORS: [(u8, u8, u8); 22] = [ - (245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - (228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice Blue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (224, 182, 184), // Candy Pink - (174, 148, 161), // Matte Pink - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; -pub const ORC_HAIR_COLORS: [(u8, u8, u8); 11] = [ - (66, 66, 59), // Wise Grey - //(107, 76, 51), // Oak Skin4 - //(203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (54, 30, 26), // Dark Skin7 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (66, 83, 113), // Mysterious Blue - (20, 19, 17), // Black -]; -pub const UNDEAD_HAIR_COLORS: [(u8, u8, u8); 22] = [ - //(245, 232, 175), // Cream Blonde - (228, 208, 147), // Gold Blonde - //(228, 223, 141), // Platinum Blonde - (199, 131, 58), // Summer Blonde - (107, 76, 51), // Oak Skin4 - (203, 154, 98), // Light Skin4 - (64, 32, 18), // Skin7 Skin4 - (86, 72, 71), // Ash Skin4 - (57, 56, 61), // Raven Black - (101, 83, 95), // Matte Purple - (101, 57, 90), // Witch Purple - (111, 54, 117), // Punky Purple - (135, 38, 39), // Dark Red - (88, 26, 29), // Wine Red - (191, 228, 254), // Ice Blue - (92, 80, 144), // Kingfisher Blue - (146, 198, 238), // Lagoon Blue - (66, 66, 59), // Decayed Grey - //(224, 182, 184), // Candy Pink - (174, 148, 161), // Matte Pink - (0, 131, 122), // Rotten Green - (163, 186, 192), // Matte Green - (84, 139, 107), // Grass Green - (48, 61, 52), // Dark Green - (20, 19, 17), // Black -]; - // Skin colors pub const DANARI_SKIN_COLORS: [Skin; 4] = [ Skin::DanariOne, @@ -352,17 +220,6 @@ pub const UNDEAD_EYE_COLORS: [EyeColor; 5] = [ ]; impl Species { - fn hair_colors(self) -> &'static [(u8, u8, u8)] { - match self { - Species::Danari => &DANARI_HAIR_COLORS, - Species::Dwarf => &DWARF_HAIR_COLORS, - Species::Elf => &ELF_HAIR_COLORS, - Species::Human => &HUMAN_HAIR_COLORS, - Species::Orc => &ORC_HAIR_COLORS, - Species::Undead => &UNDEAD_HAIR_COLORS, - } - } - fn skin_colors(self) -> &'static [Skin] { match self { Species::Danari => &DANARI_SKIN_COLORS, @@ -385,16 +242,22 @@ impl Species { } } - pub fn hair_color(self, val: u8) -> Rgb { - self.hair_colors() - .get(val as usize) - .copied() - .unwrap_or((0, 0, 0)) - .into() + /// FIXME: This is a hack! The only reason we need to do this is because + /// hair colors are currently just indices into an array, not enum + /// variants. Once we have proper variants for hair colors, we won't + /// need to do this anymore, since we will use locally defined arrays to + /// represent per-species stuff (or have some other solution for validity). + pub fn num_hair_colors(self) -> u8 { + match self { + Species::Danari => 12, + Species::Dwarf => 21, + Species::Elf => 24, + Species::Human => 22, + Species::Orc => 11, + Species::Undead => 22, + } } - pub fn num_hair_colors(self) -> u8 { self.hair_colors().len() as u8 } - pub fn skin_color(self, val: u8) -> Skin { self.skin_colors() .get(val as usize) @@ -484,238 +347,78 @@ impl Species { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum BodyType { - Female = 0, - Male = 1, -} +make_case_elim!( + body_type, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[repr(u32)] + pub enum BodyType { + Female = 0, + Male = 1, + } +); + pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male]; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum EyeColor { - VigorousBlack = 0, - NobleBlue = 1, - CuriousGreen = 2, - LoyalBrown = 3, - ViciousRed = 4, - PumpkinOrange = 5, - GhastlyYellow = 6, - MagicPurple = 7, - ToxicGreen = 8, - ExoticPurple = 9, - SulfurYellow = 10, - AmberOrange = 11, - PineGreen = 12, - CornflowerBlue = 13, -} -impl EyeColor { - pub fn light_rgb(self) -> Rgb { - match self { - EyeColor::VigorousBlack => Rgb::new(71, 59, 49), - EyeColor::NobleBlue => Rgb::new(75, 158, 191), - EyeColor::CuriousGreen => Rgb::new(110, 167, 113), - EyeColor::LoyalBrown => Rgb::new(73, 42, 36), - EyeColor::ViciousRed => Rgb::new(182, 0, 0), - EyeColor::PumpkinOrange => Rgb::new(220, 156, 19), - EyeColor::GhastlyYellow => Rgb::new(221, 225, 31), - EyeColor::MagicPurple => Rgb::new(137, 4, 177), - EyeColor::ToxicGreen => Rgb::new(1, 223, 1), - EyeColor::ExoticPurple => Rgb::new(95, 32, 111), - EyeColor::SulfurYellow => Rgb::new(235, 198, 94), - EyeColor::AmberOrange => Rgb::new(137, 46, 1), - EyeColor::PineGreen => Rgb::new(0, 78, 56), - EyeColor::CornflowerBlue => Rgb::new(18, 66, 90), - } +make_case_elim!( + eye_color, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] + #[repr(u32)] + pub enum EyeColor { + VigorousBlack = 0, + NobleBlue = 1, + CuriousGreen = 2, + LoyalBrown = 3, + ViciousRed = 4, + PumpkinOrange = 5, + GhastlyYellow = 6, + MagicPurple = 7, + ToxicGreen = 8, + ExoticPurple = 9, + SulfurYellow = 10, + AmberOrange = 11, + PineGreen = 12, + CornflowerBlue = 13, } +); - pub fn dark_rgb(self) -> Rgb { - match self { - EyeColor::VigorousBlack => Rgb::new(32, 32, 32), - EyeColor::NobleBlue => Rgb::new(62, 130, 159), - EyeColor::CuriousGreen => Rgb::new(81, 124, 84), - EyeColor::LoyalBrown => Rgb::new(54, 30, 26), - EyeColor::ViciousRed => Rgb::new(148, 0, 0), - EyeColor::PumpkinOrange => Rgb::new(209, 145, 18), - EyeColor::GhastlyYellow => Rgb::new(205, 212, 29), - EyeColor::MagicPurple => Rgb::new(110, 3, 143), - EyeColor::ToxicGreen => Rgb::new(1, 185, 1), - EyeColor::ExoticPurple => Rgb::new(69, 23, 80), - EyeColor::SulfurYellow => Rgb::new(209, 176, 84), - EyeColor::AmberOrange => Rgb::new(112, 40, 1), - EyeColor::PineGreen => Rgb::new(0, 54, 38), - EyeColor::CornflowerBlue => Rgb::new(13, 47, 64), - } +make_case_elim!( + skin, + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)] + #[repr(u32)] + pub enum Skin { + Skin1 = 0, + Skin2 = 1, + Skin3 = 2, + Skin4 = 3, + Skin5 = 4, + Skin6 = 5, + Iron = 6, + Steel = 7, + DanariOne = 8, + DanariTwo = 9, + DanariThree = 10, + DanariFour = 11, + ElfOne = 12, + ElfTwo = 13, + //ElfThree = 14, + OrcOne = 14, + OrcTwo = 15, + OrcThree = 16, + UndeadOne = 17, + UndeadTwo = 18, + UndeadThree = 19, + Skin7 = 20, + Skin8 = 21, + Skin9 = 22, + Skin10 = 23, + Skin11 = 24, + Skin12 = 25, + Skin13 = 26, + Skin14 = 27, + Skin15 = 28, + Skin16 = 29, + Skin17 = 30, + Skin18 = 31, + OrcFour = 32, } - - pub fn white_rgb(self) -> Rgb { Rgb::new(255, 255, 255) } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Accessory { - Nothing = 0, - Some = 1, -} -pub const ALL_ACCESSORIES: [Accessory; 2] = [Accessory::Nothing, Accessory::Some]; - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(u32)] -pub enum Skin { - Skin1 = 0, - Skin2 = 1, - Skin3 = 2, - Skin4 = 3, - Skin5 = 4, - Skin6 = 5, - Iron = 6, - Steel = 7, - DanariOne = 8, - DanariTwo = 9, - DanariThree = 10, - DanariFour = 11, - ElfOne = 12, - ElfTwo = 13, - //ElfThree = 14, - OrcOne = 14, - OrcTwo = 15, - OrcThree = 16, - UndeadOne = 17, - UndeadTwo = 18, - UndeadThree = 19, - Skin7 = 20, - Skin8 = 21, - Skin9 = 22, - Skin10 = 23, - Skin11 = 24, - Skin12 = 25, - Skin13 = 26, - Skin14 = 27, - Skin15 = 28, - Skin16 = 29, - Skin17 = 30, - Skin18 = 31, - OrcFour = 32, -} -impl Skin { - pub fn rgb(self) -> Rgb { - let color = match self { - Self::Skin1 => (255, 229, 200), - Self::Skin2 => (255, 218, 190), - Self::Skin3 => (255, 206, 180), - Self::Skin4 => (255, 195, 170), - Self::Skin5 => (240, 184, 160), - Self::Skin6 => (225, 172, 150), - Self::Skin7 => (210, 161, 140), - Self::Skin8 => (195, 149, 130), - Self::Skin9 => (180, 138, 120), - Self::Skin10 => (165, 126, 110), - Self::Skin11 => (150, 114, 100), - Self::Skin12 => (135, 103, 90), - Self::Skin13 => (120, 92, 80), - Self::Skin14 => (105, 80, 70), - Self::Skin15 => (90, 69, 60), - Self::Skin16 => (75, 57, 50), - Self::Skin17 => (60, 46, 40), - Self::Skin18 => (45, 34, 30), - Self::Iron => (135, 113, 95), - Self::Steel => (108, 94, 86), - Self::DanariOne => (104, 168, 196), - Self::DanariTwo => (30, 149, 201), - Self::DanariThree => (57, 120, 148), - Self::DanariFour => (40, 85, 105), - Self::ElfOne => (178, 164, 186), - Self::ElfTwo => (132, 139, 161), - //Self::ElfThree => (230, 188, 198), - Self::OrcOne => (61, 130, 42), - Self::OrcTwo => (82, 117, 36), - Self::OrcThree => (71, 94, 42), - Self::OrcFour => (97, 54, 29), - Self::UndeadOne => (240, 243, 239), - Self::UndeadTwo => (178, 178, 178), - Self::UndeadThree => (145, 135, 121), - }; - Rgb::from(color) - } - - pub fn light_rgb(self) -> Rgb { - let color = match self { - Self::Skin1 => (255, 229, 200), - Self::Skin2 => (255, 218, 190), - Self::Skin3 => (255, 206, 180), - Self::Skin4 => (255, 195, 170), - Self::Skin5 => (240, 184, 160), - Self::Skin6 => (225, 172, 150), - Self::Skin7 => (210, 161, 140), - Self::Skin8 => (195, 149, 130), - Self::Skin9 => (180, 138, 120), - Self::Skin10 => (165, 126, 110), - Self::Skin11 => (150, 114, 100), - Self::Skin12 => (135, 103, 90), - Self::Skin13 => (120, 92, 80), - Self::Skin14 => (105, 80, 70), - Self::Skin15 => (90, 69, 60), - Self::Skin16 => (75, 57, 50), - Self::Skin17 => (60, 46, 40), - Self::Skin18 => (45, 34, 30), - Self::Iron => (144, 125, 106), - Self::Steel => (120, 107, 99), - Self::DanariOne => (116, 176, 208), - Self::DanariTwo => (42, 158, 206), - Self::DanariThree => (70, 133, 160), - Self::DanariFour => (53, 96, 116), - Self::ElfOne => (190, 176, 199), //178, 164, 186 - Self::ElfTwo => (137, 144, 167), - //Self::ElfThree => (242, 199, 209), - Self::OrcOne => (83, 165, 56), - Self::OrcTwo => (92, 132, 46), - Self::OrcThree => (84, 110, 54), - Self::OrcFour => (97, 54, 29), - Self::UndeadOne => (254, 252, 251), - Self::UndeadTwo => (190, 192, 191), - Self::UndeadThree => (160, 151, 134), - }; - Rgb::from(color) - } - - pub fn dark_rgb(self) -> Rgb { - let color = match self { - Self::Skin1 => (242, 217, 189), - Self::Skin2 => (242, 207, 189), - Self::Skin3 => (242, 197, 172), - Self::Skin4 => (242, 186, 162), - Self::Skin5 => (212, 173, 150), - Self::Skin6 => (212, 163, 142), - Self::Skin7 => (196, 151, 132), - Self::Skin8 => (181, 139, 121), - Self::Skin9 => (168, 129, 113), - Self::Skin10 => (153, 117, 103), - Self::Skin11 => (138, 105, 92), - Self::Skin12 => (122, 93, 82), - Self::Skin13 => (107, 82, 72), - Self::Skin14 => (92, 70, 62), - Self::Skin15 => (77, 59, 51), - Self::Skin16 => (61, 47, 41), - Self::Skin17 => (48, 37, 32), - Self::Skin18 => (33, 25, 22), - Self::Iron => (124, 99, 82), - Self::Steel => (96, 81, 72), - Self::DanariOne => (92, 155, 183), - Self::DanariTwo => (25, 142, 192), - Self::DanariThree => (52, 115, 143), - Self::DanariFour => (34, 80, 99), - Self::ElfOne => (170, 155, 175), //170, 157, 179 - Self::ElfTwo => (126, 132, 153), - //Self::ElfThree => (217, 178, 187), - Self::OrcOne => (55, 114, 36), - Self::OrcTwo => (70, 104, 29), - Self::OrcThree => (60, 83, 32), - Self::OrcFour => (84, 47, 25), - Self::UndeadOne => (229, 231, 230), - Self::UndeadTwo => (165, 166, 164), - Self::UndeadThree => (130, 122, 106), - }; - Rgb::from(color) - } -} +); diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 7f7eef651d..26b490a7f4 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -82,7 +82,8 @@ impl CharacterState { | CharacterState::BasicBlock | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) - | CharacterState::ChargedRanged(_)) + | CharacterState::ChargedRanged(_) + ) } pub fn is_attack(&self) -> bool { @@ -93,7 +94,8 @@ impl CharacterState { | CharacterState::TripleStrike(_) | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) - | CharacterState::ChargedRanged(_)) + | CharacterState::ChargedRanged(_) + ) } pub fn is_aimed(&self) -> bool { @@ -104,7 +106,8 @@ impl CharacterState { | CharacterState::TripleStrike(_) | CharacterState::BasicBlock | CharacterState::LeapMelee(_) - | CharacterState::ChargedRanged(_)) + | CharacterState::ChargedRanged(_) + ) } pub fn is_block(&self) -> bool { matches!(self, CharacterState::BasicBlock) } diff --git a/common/src/lib.rs b/common/src/lib.rs index f5bf9ef71a..dacdedd767 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,6 +3,8 @@ #![type_length_limit = "1664759"] #![feature( arbitrary_enum_discriminant, + const_checked_int_methods, + fundamental, option_unwrap_none, bool_to_option, label_break_value, @@ -37,6 +39,7 @@ pub mod store; pub mod sync; pub mod sys; pub mod terrain; +pub mod typed; pub mod util; pub mod vol; pub mod volumes; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index d6d55e46e8..6b8904d892 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -48,6 +48,127 @@ pub struct CharacterInfo { pub level: u32, } +#[derive(Debug, Clone, Serialize, Deserialize)] +/// World map information. Note that currently, we always send the whole thing +/// in one go, but the structure aims to try to provide information as locally +/// as possible, so that in the future we can split up large maps into multiple +/// WorldMapMsg fragments. +/// +/// TODO: Update message format to make fragmentable, allowing us to send more +/// information without running into bandwidth issues. +/// +/// TODO: Add information for rivers (currently, we just prerender them on the +/// server, but this is not a great solution for LoD. The map rendering code is +/// already set up to be able to take advantage of the river rendering being +/// split out, but the format is a little complicated for space reasons and it +/// may take some tweaking to get right, so we avoid sending it for now). +/// +/// TODO: measure explicit compression schemes that might save space, e.g. +/// repeating the "small angles" optimization that works well on more detailed +/// shadow maps intended for height maps. +pub struct WorldMapMsg { + /// Log base 2 of world map dimensions (width × height) in chunks. + /// + /// NOTE: Invariant: chunk count fits in a u16. + pub dimensions_lg: Vec2, + /// Sea level (used to provide a base altitude). + pub sea_level: f32, + /// Max height (used to scale altitudes). + pub max_height: f32, + /// RGB+A; the alpha channel is currently unused, but will be used in the + /// future. Entries are in the usual chunk order. + pub rgba: Vec, + /// Altitudes: bits 2 to 0 are unused, then bits 15 to 3 are used for + /// altitude. The remainder are currently unused, but we have plans to + /// use 7 bits for water depth (using an integer f7 encoding), and we + /// will find other uses for the remaining 12 bits. + pub alt: Vec, + /// Horizon mapping. This is a variant of shadow mapping that is + /// specifically designed for height maps; it takes advantage of their + /// regular structure (e.g. no holes) to compress all information needed + /// to decide when to cast a sharp shadow into a single nagle, the "horizon + /// angle." This is the smallest angle with the ground at which light can + /// pass through any occluders to reach the chunk, in some chosen + /// horizontal direction. This would not be sufficient for a more + /// complicated 3D structure, but it works for height maps since: + /// + /// 1. they have no gaps, so as soon as light can shine through it will + /// always be able to do so, and + /// 2. we only care about lighting from the top, and only from the east and + /// west (since at a large scale like this we mostly just want to + /// handle variable sunlight; moonlight would present more challenges + /// but we currently have no plans to try to cast accurate shadows in + /// moonlight). + /// + /// Our chosen format is two pairs of vectors, + /// with the first pair representing west-facing light (casting shadows on + /// the left side) and the second representing east-facing light + /// (casting shadows on the east side). + /// + /// The pair of vectors consists of (with each vector in the usual chunk + /// order): + /// + /// * Horizon angle pointing east (1 byte, scaled so 1 unit = 255° / 360). + /// We might consider switching to tangent if that represents the + /// information we care about better. + /// * Approximate (floor) height of maximal occluder. We currently use this + /// to try to deliver some approximation of soft shadows, which isn't that + /// big a deal on the world map but is probably needed in order to ensure + /// smooth transitions between chunks in LoD view. Additionally, when we + /// start using the shadow information to do local lighting on the world + /// map, we'll want a quick way to test where we can go out of shadoow at + /// arbitrary heights (since the player and other entities cajn find + /// themselves far from the ground at times). While this is only an + /// approximation to a proper distance map, hopefully it will give us + /// something that feels reasonable enough for Veloren's style. + /// + /// NOTE: On compression. + /// + /// Horizon mapping has a lot of advantages for height maps (simple, easy to + /// understand, doesn't require any fancy math or approximation beyond + /// precision loss), though it loses a few of them by having to store + /// distance to occluder as well. However, just storing tons + /// and tons of regular shadow maps (153 for a full day cycle, stored at + /// irregular intervals) combined with clever explicit compression and + /// avoiding recording sharp local shadows (preferring retracing for + /// these), yielded a compression rate of under 3 bits per column! Since + /// we likely want to avoid per-column shadows for worlds of the sizes we + /// want, we'd still need to store *some* extra information to create + /// soft shadows, but it would still be nice to try to drive down our + /// size as much as possible given how compressible shadows of height + /// maps seem to be in practice. Therefore, we try to take advantage of the + /// way existing compression algorithms tend to work to see if we can + /// achieve significant gains without doing a lot of custom work. + /// + /// Specifically, since our rays are cast east/west, we expect that for each + /// row, the horizon angles in each direction should be sequences of + /// monotonically increasing values (as chunks approach a tall + /// occluder), followed by sequences of no shadow, repeated + /// until the end of the map. Monotonic sequences and same-byte sequences + /// are usually easy to compress and existing algorithms are more likely + /// to be able to deal with them than jumbled data. If we were to keep + /// both directions in the same vector, off-the-shelf compression would + /// probably be less effective. + /// + /// For related reasons, rather than storing distances as in a standard + /// distance map (which would lead to monotonically *decreaing* values + /// as we approached the occluder from a given direction), we store the + /// estimated *occluder height.* The idea here is that we replace the + /// monotonic sequences with constant sequences, which are extremely + /// straightforward to compress and mostly handled automatically by anything + /// that does run-length encoding (i.e. most off-the-shelf compression + /// algorithms). + /// + /// We still need to benchmark this properly, as there's no guarantee our + /// current compression algorithms will actually work well on this data + /// in practice. It's possible that some other permutation (e.g. more + /// bits reserved for "distance to occluder" in exchange for an even + /// more predictible sequence) would end up compressing better than storing + /// angles, or that we don't need as much precision as we currently have + /// (256 possible angles). + pub horizons: [(Vec, Vec); 2], +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub enum InviteAnswer { Accepted, @@ -68,7 +189,7 @@ pub enum ServerMsg { server_info: ServerInfo, time_of_day: state::TimeOfDay, max_group_size: u32, - world_map: (Vec2, Vec), + world_map: WorldMapMsg, recipe_book: RecipeBook, }, /// An error occurred while loading character data diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 72717bd0fb..19e5499dbd 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -453,10 +453,10 @@ pub struct Block { } impl Block { - pub fn new(kind: BlockKind, color: Rgb) -> Self { + pub const fn new(kind: BlockKind, color: Rgb) -> Self { Self { kind, - color: color.into_array(), + color: [color.r, color.g, color.b], } } diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs new file mode 100644 index 0000000000..e8977fb1bb --- /dev/null +++ b/common/src/terrain/map.rs @@ -0,0 +1,713 @@ +use super::{ + neighbors, quadratic_nearest_point, river_spline_coeffs, uniform_idx_as_vec2, + vec2_as_uniform_idx, TerrainChunkSize, NEIGHBOR_DELTA, TERRAIN_CHUNK_BLOCKS_LG, +}; +use crate::vol::RectVolSize; +use core::{f32, f64, iter, ops::RangeInclusive}; +use vek::*; + +/// Base two logarithm of the maximum size of the precomputed world, in meters, +/// along the x (E/W) and y (N/S) dimensions. +/// +/// NOTE: Each dimension is guaranteed to be a power of 2, so the logarithm is +/// exact. This is so that it is possible (at least in theory) for compiler or +/// runtime optimizations exploiting this are possible. For example, division +/// by the chunk size can turn into a bit shift. +/// +/// NOTE: As an invariant, this value is at least [TERRAIN_CHUNK_BLOCKS_LG]. +/// +/// NOTE: As an invariant, `(1 << [MAX_WORLD_BLOCKS_LG])` fits in an i32. +/// +/// TODO: Add static assertions for the above invariants. +/// +/// Currently, we define the maximum to be 19 (corresponding to 2^19 m) for both +/// directions. This value was derived by backwards reasoning from the following +/// conservative estimate of the maximum landmass area (using an approximation +/// of 1024 blocks / km instead of 1000 blocks / km, which will result in an +/// estimate that is strictly lower than the real landmass): +/// +/// Max area (km²) +/// ≌ (2^19 blocks * 1 km / 1024 blocks)^2 +/// = 2^((19 - 10) * 2) km² +/// = 2^18 km² +/// = 262,144 km² +/// +/// which is roughly the same area as the entire United Kingdom, and twice the +/// horizontal extent of Dwarf Fortress's largest map. Besides the comparison +/// to other games without infinite or near-infinite maps (like Dwarf Fortress), +/// there are other reasons to choose this as a good maximum size: +/// +/// * It is large enough to include geological features of fairly realistic +/// scale. It may be hard to do justice to truly enormous features like the +/// Amazon River, and natural temperature variation not related to altitude +/// would probably not produce climate extremes on an Earth-like planet, but +/// it can comfortably fit enormous river basins, Everest-scale mountains, +/// large islands and inland lakes, vast forests and deserts, and so on. +/// +/// * It is large enough that making it from one side of the map to another will +/// take a *very* long time. We show this with two examples. In each +/// example, travel is either purely horizontal or purely vertical (to +/// minimize distance traveled) across the whole map, and we assume there are +/// no obstacles or slopes. +/// +/// In example 1, a human is walking at the (real-time) speed of the fastest +/// marathon runners (around 6 blocks / real-time s). We assume the human can +/// maintain this pace indefinitely without stopping. Then crossing the map +/// will take about: +/// +/// 2^19 blocks * 1 real-time s / 6 blocks * 1 real-time min / 60 real-time s +/// * 1 real-time hr / 60 real-time min * 1 real-time days / 24 hr = 2^19 / 6 / +/// 60 / 60 / 24 real-time days ≌ 1 real-time day. +/// +/// That's right--it will take a full day of *real* time to cross the map at +/// an apparent speed of 6 m / s. Moreover, since in-game time passes at a +/// rate of 1 in-game min / 1 in-game s, this would also take *60 days* of +/// in-game time. +/// +/// Still though, this is the rate of an ordinary human. And besides that, if +/// we instead had a marathon runner traveling at 6 m / in-game s, it would +/// take just 1 day of in-game time for the runner to cross the map, or a mere +/// 0.4 hr of real time. To show that this rate of travel is unrealistic (and +/// perhaps make an eventual argument for a slower real-time to in-game time +/// conversion rate), our second example will consist of a high-speed train +/// running at 300 km / real-time h (the fastest real-world high speed train +/// averages under 270 k m / h, with 300 km / h as the designed top speed). +/// For a train traveling at this apparent speed (in real time), crossing the +/// map would take: +/// +/// 2^19 blocks * 1 km / 1000 blocks * 1 real-time hr / 300 km +/// = 2^19 / 1000 / 300 real-time hr +/// ≌ 1.75 real-time hr +/// +/// = 2^19 / 1000 / 300 real-time hr * 60 in-game hr / real-time hr +/// * 1 in-game days / 24 in-game hr +/// = 2^19 / 1000 / 300 * 60 / 24 in-game days +/// ≌ 4.37 in-game days +/// +/// In other words, something faster in real-time than any existing high-speed +/// train would be over 4 times slower (in real-time) than our hypothetical +/// top marathon runner running at 6 m / s in in-game speed. This suggests +/// that the gap between in-game time and real-time is probably much too large +/// for most purposes; however, what it definitely shows is that even +/// extremely fast in-game transport across the world will not trivialize its +/// size. +/// +/// It follows that cities or towns of realistic scale, player housing, +/// fields, and so on, will all fit comfortably on a map of this size, while +/// at the same time still being reachable by non-warping, in-game mechanisms +/// (such as high-speed transit). It also provides plenty of room for mounts +/// of varying speeds, which can help ensure that players don't feel cramped or +/// deliberately slowed down by their own speed. +/// +/// * It is small enough that it is (barely) plausible that we could still +/// generate maps for a world of this size using detailed and realistic +/// erosion algorithms. At 1/4 of this map size along each dimension, +/// generation currently takes around 5 hours on a good computer, and one +/// could imagine (since the bottleneck step appears to be roughly O(n)) that +/// with a smart implementation generation times of under a week could be +/// achievable. +/// +/// * The map extends further than the resolution of human eyesight under +/// Earthlike conditions, even from tall mountains across clear landscapes. +/// According to one calculation, even from Mt. Everest in the absence of +/// cloud cover, you could only see for about 339 km before the Earth's +/// horizon prevented you from seeing further, and other sources suggest that +/// in practice the limit is closer to 160 km under realistic conditions. This +/// implies that making the map much larger in a realistic way would require +/// incorporating curvature, and also implies that any features that cannot +/// fit on the map would not (under realistic atmospheric conditions) be fully +/// visible from any point on Earth. Therefore, even if we cannot represent +/// features larger than this accurately, nothing should be amiss from a +/// visual perspective, so this should not significantly impact the player +/// experience. +pub const MAX_WORLD_BLOCKS_LG: Vec2 = Vec2 { x: 19, y: 19 }; + +/// Base two logarithm of a world size, in chunks, per dimension +/// (each dimension must be a power of 2, so the logarithm is exact). +/// +/// NOTE: As an invariant, each dimension must be between 0 and +/// `[MAX_WORLD_BLOCKS_LG] - [TERRAIN_CHUNK_BLOCKS_LG]`. +/// +/// NOTE: As an invariant, `(1 << ([DEFAULT_WORLD_CHUNKS_LG] + +/// [TERRAIN_CHUNK_BLOCKS_LG]))` fits in an i32 (derived from the invariant +/// on [MAX_WORLD_BLOCKS_LG]). +/// +/// NOTE: As an invariant, each dimension (in chunks) must fit in a u16. +/// +/// NOTE: As an invariant, the product of dimensions (in chunks) must fit in a +/// usize. +/// +/// These invariants are all checked on construction of a `MapSizeLg`. +#[derive(Clone, Copy, Debug)] +pub struct MapSizeLg(Vec2); + +impl MapSizeLg { + // FIXME: We cannot use is_some() here because it is not currently marked as a + // `const fn`. Since being able to use conditionals in constant expressions has + // not technically been stabilized yet, Clippy probably doesn't check for this + // case yet. When it can, or when is_some() is stabilized as a `const fn`, + // we should deal with this. + #[allow(clippy::redundant_pattern_matching)] + /// Construct a new `MapSizeLg`, returning an error if the needed invariants + /// do not hold and the vector otherwise. + /// + /// TODO: In the future, we may use unsafe code to assert to the compiler + /// that these invariants indeed hold, safely opening up optimizations + /// that might not otherwise be available at runtime. + #[inline(always)] + pub const fn new(map_size_lg: Vec2) -> Result { + // Assertion on dimensions: must be between + // 0 and ([MAX_WORLD_BLOCKS_LG] - [TERRAIN_CHUNK_BLOCKS_LG]) + let is_le_max = map_size_lg.x <= MAX_WORLD_BLOCKS_LG.x - TERRAIN_CHUNK_BLOCKS_LG + && map_size_lg.y <= MAX_WORLD_BLOCKS_LG.y - TERRAIN_CHUNK_BLOCKS_LG; + // Assertion on dimensions: chunks must fit in a u16. + let chunks_in_range = + /* 1u16.checked_shl(map_size_lg.x).is_some() && + 1u16.checked_shl(map_size_lg.y).is_some(); */ + map_size_lg.x <= 16 && + map_size_lg.y <= 16; + if is_le_max && chunks_in_range { + // Assertion on dimensions: blocks must fit in a i32. + let blocks_in_range = + /* 1i32.checked_shl(map_size_lg.x + TERRAIN_CHUNK_BLOCKS_LG).is_some() && + 1i32.checked_shl(map_size_lg.y + TERRAIN_CHUNK_BLOCKS_LG).is_some(); */ + map_size_lg.x + TERRAIN_CHUNK_BLOCKS_LG < 32 && + map_size_lg.y + TERRAIN_CHUNK_BLOCKS_LG < 32; + // Assertion on dimensions: product of dimensions must fit in a usize. + let chunks_product_in_range = + matches!(1usize.checked_shl(map_size_lg.x + map_size_lg.y), Some(_)); + if blocks_in_range && chunks_product_in_range { + // Cleared all invariants. + Ok(MapSizeLg(map_size_lg)) + } else { + Err(()) + } + } else { + Err(()) + } + } + + #[inline(always)] + /// Acquire the `MapSizeLg`'s inner vector. + pub const fn vec(self) -> Vec2 { self.0 } + + #[inline(always)] + /// Get the size of this map in chunks. + pub const fn chunks(self) -> Vec2 { Vec2::new(1 << self.0.x, 1 << self.0.y) } + + /// Get the size of an array of the correct size to hold all chunks. + pub const fn chunks_len(self) -> usize { 1 << (self.0.x + self.0.y) } +} + +impl From for Vec2 { + #[inline(always)] + fn from(size: MapSizeLg) -> Self { size.vec() } +} + +pub struct MapConfig<'a> { + /// Base two logarithm of the chunk dimensions of the base map. + /// Has no default; set explicitly during initial orthographic projection. + pub map_size_lg: MapSizeLg, + /// Dimensions of the window being written to. + /// + /// Defaults to `1 << [MapConfig::map_size_lg]`. + pub dimensions: Vec2, + /// x, y, and z of top left of map. + /// + /// Default x and y are 0.0; no reasonable default for z, so set during + /// initial orthographic projection. + pub focus: Vec3, + /// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain + /// makes smaller differences in altitude appear larger. + /// + /// No reasonable default for z; set during initial orthographic projection. + pub gain: f32, + /// `fov` is used for shading purposes and refers to how much impact a + /// change in the z direction has on the perceived slope relative to the + /// same change in x and y. + /// + /// It is stored as cos θ in the range (0, 1\] where θ is the FOV + /// "half-angle" used for perspective projection. At 1.0, we treat it + /// as the limit value for θ = 90 degrees, and use an orthographic + /// projection. + /// + /// Defaults to 1.0. + /// + /// FIXME: This is a hack that tries to incorrectly implement a variant of + /// perspective projection (which generates ∂P/∂x and ∂P/∂y for screen + /// coordinate P by using the hyperbolic function \[assuming frustum of + /// \[l, r, b, t, n, f\], rh coordinates, and output from -1 to 1 in + /// s/t, 0 to 1 in r, and NDC is left-handed \[so visible z ranges from + /// -n to -f\]\]): + /// + /// P.s(x, y, z) = -1 + 2(-n/z x - l) / ( r - l) + /// P.t(x, y, z) = -1 + 2(-n/z y - b) / ( t - b) + /// P.r(x, y, z) = 0 + -f(-n/z - 1) / ( f - n) + /// + /// Then arbitrarily using W_e_x = (r - l) as the width of the projected + /// image, we have W_e_x = 2 n_e tan θ ⇒ tan Θ = (r - l) / (2n_e), for a + /// perspective projection + /// + /// (where θ is the half-angle of the FOV). + /// + /// Taking the limit as θ → 90, we show that this degenrates to an + /// orthogonal projection: + /// + /// lim{n → ∞}(-f(-n / z - 1) / (f - n)) = -(z - -n) / (f - n). + /// + /// (Proof not currently included, but has been formalized for the P.r case + /// in Coq-tactic notation; the proof can be added on request, but is + /// large and probably not well-suited to Rust documentation). + /// + /// For this reason, we feel free to store `fov` as cos θ in the range (0, + /// 1\]. + /// + /// However, `fov` does not actually work properly yet, so for now we just + /// treat it as a visual gimmick. + pub fov: f64, + /// Scale is like gain, but for x and y rather than z. + /// + /// Defaults to (1 << world_size_lg).x / dimensions.x (NOTE: fractional, not + /// integer, division!). + pub scale: f64, + /// Vector that indicates which direction light is coming from, if shading + /// is turned on. + /// + /// Right-handed coordinate system: light is going left, down, and + /// "backwards" (i.e. on the map, where we translate the y coordinate on + /// the world map to z in the coordinate system, the light comes from -y + /// on the map and points towards +y on the map). In a right + /// handed coordinate system, the "camera" points towards -z, so positive z + /// is backwards "into" the camera. + /// + /// "In world space the x-axis will be pointing east, the y-axis up and the + /// z-axis will be pointing south" + /// + /// Defaults to (-0.8, -1.0, 0.3). + pub light_direction: Vec3, + /// If Some, uses the provided horizon map. + /// + /// Defaults to None. + pub horizons: Option<&'a [(Vec, Vec); 2]>, + /// If true, only the basement (bedrock) is used for altitude; otherwise, + /// the surface is used. + /// + /// Defaults to false. + pub is_basement: bool, + /// If true, water is rendered; otherwise, the surface without water is + /// rendered, even if it is underwater. + /// + /// Defaults to true. + pub is_water: bool, + /// If true, 3D lighting and shading are turned on. Otherwise, a plain + /// altitude map is used. + /// + /// Defaults to true. + pub is_shaded: bool, + /// If true, the red component of the image is also used for temperature + /// (redder is hotter). Defaults to false. + pub is_temperature: bool, + /// If true, the blue component of the image is also used for humidity + /// (bluer is wetter). + /// + /// Defaults to false. + pub is_humidity: bool, + /// Record debug information. + /// + /// Defaults to false. + pub is_debug: bool, +} + +pub const QUADRANTS: usize = 4; + +pub struct MapDebug { + pub quads: [[u32; QUADRANTS]; QUADRANTS], + pub rivers: u32, + pub lakes: u32, + pub oceans: u32, +} + +/// Connection kind (per edge). Currently just supports rivers, but may be +/// extended to support paths or at least one other kind of connection. +#[derive(Clone, Copy, Debug)] +pub enum ConnectionKind { + /// Connection forms a visible river. + River, +} + +/// Map connection (per edge). +#[derive(Clone, Copy, Debug)] +pub struct Connection { + /// The kind of connection this is (e.g. river or path). + pub kind: ConnectionKind, + /// Assumed to be the "b" part of a 2d quadratic function. + pub spline_derivative: Vec2, + /// Width of the connection. + pub width: f32, +} + +/// Per-chunk data the map needs to be able to sample in order to correctly +/// render. +#[derive(Clone, Debug)] +pub struct MapSample { + /// the base RGB color for a particular map pixel using the current settings + /// (i.e. the color *without* lighting). + pub rgb: Rgb, + /// Surface altitude information + /// (correctly reflecting settings like is_basement and is_water) + pub alt: f64, + /// Downhill chunk (may not be meaningful on ocean tiles, or at least edge + /// tiles) + pub downhill_wpos: Vec2, + /// Connection information about any connections to/from this chunk (e.g. + /// rivers). + /// + /// Connections at each index correspond to the same index in + /// NEIGHBOR_DELTA. + pub connections: Option<[Option; 8]>, +} + +impl<'a> MapConfig<'a> { + /// Constructs the configuration settings for an orthographic projection of + /// a map from the top down, rendering (by default) the complete map to + /// an image such that the chunk:pixel ratio is 1:1. + /// + /// Takes two arguments: the base two logarithm of the horizontal map extent + /// (in chunks), and the z bounds of the projection. + pub fn orthographic(map_size_lg: MapSizeLg, z_bounds: RangeInclusive) -> Self { + assert!(z_bounds.start() <= z_bounds.end()); + // NOTE: Safe cast since map_size_lg is restricted by the prior assert. + let dimensions = map_size_lg.chunks().map(usize::from); + Self { + map_size_lg, + dimensions, + focus: Vec3::new(0.0, 0.0, f64::from(*z_bounds.start())), + gain: z_bounds.end() - z_bounds.start(), + fov: 1.0, + scale: 1.0, + light_direction: Vec3::new(-1.2, -1.0, 0.8), + horizons: None, + + is_basement: false, + is_water: true, + is_shaded: true, + is_temperature: false, + is_humidity: false, + is_debug: false, + } + } + + /// Get the base 2 logarithm of the underlying map size. + pub fn map_size_lg(&self) -> MapSizeLg { self.map_size_lg } + + /// Generates a map image using the specified settings. Note that it will + /// write from left to write from (0, 0) to dimensions - 1, inclusive, + /// with 4 1-byte color components provided as (r, g, b, a). It is up + /// to the caller to provide a function that translates this information + /// into the correct format for a buffer and writes to it. + /// + /// sample_pos is a function that, given a chunk position, returns enough + /// information about the chunk to attempt to render it on the map. + /// When in doubt, try using `MapConfig::sample_pos` for this. + /// + /// sample_wpos is a simple function that, given a *column* position, + /// returns the approximate altitude at that column. When in doubt, try + /// using `MapConfig::sample_wpos` for this. + #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 + #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 + #[allow(clippy::many_single_char_names)] + pub fn generate( + &self, + sample_pos: impl Fn(Vec2) -> MapSample, + sample_wpos: impl Fn(Vec2) -> f32, + mut write_pixel: impl FnMut(Vec2, (u8, u8, u8, u8)), + ) -> MapDebug { + let MapConfig { + map_size_lg, + dimensions, + focus, + gain, + fov, + scale, + light_direction, + horizons, + + is_shaded, + // is_debug, + .. + } = *self; + + let light_direction = Vec3::new( + light_direction.x, + light_direction.y, + 0.0, // we currently ignore light_direction.z. + ); + let light_shadow_dir = if light_direction.x >= 0.0 { 0 } else { 1 }; + let horizon_map = horizons.map(|horizons| &horizons[light_shadow_dir]); + let light = light_direction.normalized(); + let /*mut */quads = [[0u32; QUADRANTS]; QUADRANTS]; + let /*mut */rivers = 0u32; + let /*mut */lakes = 0u32; + let /*mut */oceans = 0u32; + + let focus_rect = Vec2::from(focus); + + let chunk_size = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); + + /* // NOTE: Asserting this to enable LLVM optimizations. Ideally we should come up + // with a principled way to do this (especially one with no runtime + // cost). + assert!( + map_size_lg + .vec() + .cmple(&(MAX_WORLD_BLOCKS_LG - TERRAIN_CHUNK_BLOCKS_LG)) + .reduce_and() + ); */ + let world_size = map_size_lg.chunks(); + + (0..dimensions.y * dimensions.x).for_each(|chunk_idx| { + let i = chunk_idx % dimensions.x as usize; + let j = chunk_idx / dimensions.x as usize; + + let wposf = focus_rect + Vec2::new(i as f64, j as f64) * scale; + let pos = wposf.map(|e: f64| e as i32); + let wposf = wposf * chunk_size; + + let chunk_idx = if pos.reduce_partial_min() >= 0 + && pos.x < world_size.x as i32 + && pos.y < world_size.y as i32 + { + Some(vec2_as_uniform_idx(map_size_lg, pos)) + } else { + None + }; + + let MapSample { + rgb, + alt, + downhill_wpos, + .. + } = sample_pos(pos); + + let alt = alt as f32; + let wposi = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let mut rgb = rgb.map(|e| e as f64 / 255.0); + + // Material properties: + // + // For each material in the scene, + // k_s = (RGB) specular reflection constant + let mut k_s = Rgb::new(1.0, 1.0, 1.0); + // k_d = (RGB) diffuse reflection constant + let mut k_d = rgb; + // k_a = (RGB) ambient reflection constant + let mut k_a = rgb; + // α = (per-material) shininess constant + let mut alpha = 4.0; // 4.0; + + // Compute connections + let mut has_river = false; + // NOTE: consider replacing neighbors with local_cells, since it is more + // accurate (though I'm not sure if it can matter for these + // purposes). + chunk_idx + .into_iter() + .flat_map(|chunk_idx| { + neighbors(map_size_lg, chunk_idx).chain(iter::once(chunk_idx)) + }) + .for_each(|neighbor_posi| { + let neighbor_pos = uniform_idx_as_vec2(map_size_lg, neighbor_posi); + let neighbor_wpos = neighbor_pos.map(|e| e as f64) * chunk_size; + let MapSample { connections, .. } = sample_pos(neighbor_pos); + NEIGHBOR_DELTA + .iter() + .zip(connections.iter().flatten()) + .for_each(|(&delta, connection)| { + let connection = if let Some(connection) = connection { + connection + } else { + return; + }; + let downhill_wpos = neighbor_wpos + + Vec2::from(delta).map(|e: i32| e as f64) * chunk_size; + let coeffs = river_spline_coeffs( + neighbor_wpos, + connection.spline_derivative, + downhill_wpos, + ); + let (_t, _pt, dist) = if let Some((t, pt, dist)) = + quadratic_nearest_point(&coeffs, wposf) + { + (t, pt, dist) + } else { + let ndist = wposf.distance_squared(neighbor_wpos); + let ddist = wposf.distance_squared(downhill_wpos); + if ndist <= ddist { + (0.0, neighbor_wpos, ndist) + } else { + (1.0, downhill_wpos, ddist) + } + }; + let connection_dist = + (dist.sqrt() - (connection.width as f64 * 0.5).max(1.0)).max(0.0); + if connection_dist == 0.0 { + match connection.kind { + ConnectionKind::River => { + has_river = true; + }, + } + } + }); + }); + + // Color in connectins. + let water_color_factor = 2.0; + let g_water = 32.0 * water_color_factor; + let b_water = 64.0 * water_color_factor; + if has_river { + let water_rgb = Rgb::new(0, ((g_water) * 1.0) as u8, ((b_water) * 1.0) as u8) + .map(|e| e as f64 / 255.0); + rgb = water_rgb; + k_s = Rgb::new(1.0, 1.0, 1.0); + k_d = water_rgb; + k_a = water_rgb; + alpha = 0.255; + } + + let downhill_alt = sample_wpos(downhill_wpos); + let cross_pos = wposi + + ((downhill_wpos - wposi) + .map(|e| e as f32) + .rotated_z(f32::consts::FRAC_PI_2) + .map(|e| e as i32)); + let cross_alt = sample_wpos(cross_pos); + // TODO: Fix use of fov to match proper perspective projection, as described in + // the doc comment. + // Pointing downhill, forward + // (index--note that (0,0,1) is backward right-handed) + let forward_vec = Vec3::new( + (downhill_wpos.x - wposi.x) as f64, + ((downhill_alt - alt) * gain) as f64 * fov, + (downhill_wpos.y - wposi.y) as f64, + ); + // Pointing 90 degrees left (in horizontal xy) of downhill, up + // (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed) + let up_vec = Vec3::new( + (cross_pos.x - wposi.x) as f64, + ((cross_alt - alt) * gain) as f64 * fov, + (cross_pos.y - wposi.y) as f64, + ); + // let surface_normal = Vec3::new(fov* (f.y * u.z - f.z * u.y), -(f.x * u.z - + // f.z * u.x), fov* (f.x * u.y - f.y * u.x)).normalized(); + // Then cross points "to the right" (upwards) on a right-handed coordinate + // system. (right-handed coordinate system means (0, 0, 1.0) is + // "forward" into the screen). + let surface_normal = forward_vec.cross(up_vec).normalized(); + + // TODO: Figure out if we can reimplement debugging. + /* if is_debug { + let quad = + |x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1); + if river_kind.is_none() || humidity != 0.0 { + quads[quad(humidity)][quad(temperature)] += 1; + } + match river_kind { + Some(RiverKind::River { .. }) => { + rivers += 1; + }, + Some(RiverKind::Lake { .. }) => { + lakes += 1; + }, + Some(RiverKind::Ocean { .. }) => { + oceans += 1; + }, + None => {}, + } + } */ + + let shade_frac = horizon_map + .and_then(|(angles, heights)| { + chunk_idx + .and_then(|chunk_idx| angles.get(chunk_idx)) + .map(|&e| (e as f64, heights)) + }) + .and_then(|(e, heights)| { + chunk_idx + .and_then(|chunk_idx| heights.get(chunk_idx)) + .map(|&f| (e, f as f64)) + }) + .map(|(angle, height)| { + let w = 0.1; + let height = (height - f64::from(alt * gain)).max(0.0); + if angle != 0.0 && light_direction.x != 0.0 && height != 0.0 { + let deltax = height / angle; + let lighty = (light_direction.y / light_direction.x * deltax).abs(); + let deltay = lighty - height; + let s = (deltay / deltax / w).min(1.0).max(0.0); + // Smoothstep + s * s * (3.0 - 2.0 * s) + } else { + 1.0 + } + }) + .unwrap_or(1.0); + + let rgb = if is_shaded { + // Phong reflection model with shadows: + // + // I_p = k_a i_a + shadow * Σ {m ∈ lights} (k_d (L_m ⋅ N) i_m,d + k_s (R_m ⋅ + // V)^α i_m,s) + // + // where for the whole scene, + // i_a = (RGB) intensity of ambient lighting component + let i_a = Rgb::new(0.1, 0.1, 0.1); + // V = direction pointing towards the viewer (e.g. virtual camera). + let v = Vec3::new(0.0, 0.0, -1.0).normalized(); + + // for each light m, + // i_m,d = (RGB) intensity of diffuse component of light source m + let i_m_d = Rgb::new(1.0, 1.0, 1.0); + // i_m,s = (RGB) intensity of specular component of light source m + let i_m_s = Rgb::new(0.45, 0.45, 0.45); + + // for each light m and point p, + // L_m = (normalized) direction vector from point on surface to light source m + let l_m = light; + // N = (normalized) normal at this point on the surface, + let n = surface_normal; + // R_m = (normalized) direction a perfectly reflected ray of light from m would + // take from point p = 2(L_m ⋅ N)N - L_m + let r_m = (-l_m).reflected(n); // 2 * (l_m.dot(n)) * n - l_m; + // + // and for each point p in the scene, + // shadow = computed shadow factor at point p + // FIXME: Should really just be shade_frac, but with only ambient light we lose + // all local lighting detail... some sort of global illumination (e.g. + // radiosity) is of course the "right" solution, but maybe we can find + // something cheaper? + let shadow = 0.2 + 0.8 * shade_frac; + + let lambertian = l_m.dot(n).max(0.0); + let spec_angle = r_m.dot(v).max(0.0); + + let ambient = k_a * i_a; + let diffuse = k_d * lambertian * i_m_d; + let specular = k_s * spec_angle.powf(alpha) * i_m_s; + (ambient + shadow * (diffuse + specular)).map(|e| e.min(1.0)) + } else { + rgb + } + .map(|e| (e * 255.0) as u8); + + let rgba = (rgb.r, rgb.g, rgb.b, 255); + write_pixel(Vec2::new(i, j), rgba); + }); + + MapDebug { + quads, + rivers, + lakes, + oceans, + } + } +} diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 55e7b56013..ada6b7ab85 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -1,14 +1,17 @@ pub mod biome; pub mod block; pub mod chonk; +pub mod map; pub mod structure; // Reexports pub use self::{ biome::BiomeKind, block::{Block, BlockKind}, + map::MapSizeLg, structure::Structure, }; +use roots::find_roots_cubic; use serde::{Deserialize, Serialize}; use crate::{vol::RectVolSize, volumes::vol_grid_2d::VolGrid2d}; @@ -19,8 +22,24 @@ use vek::*; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TerrainChunkSize; +/// Base two logarithm of the number of blocks along either horizontal axis of +/// a chunk. +/// +/// NOTE: (1 << CHUNK_SIZE_LG) is guaranteed to fit in a u32. +/// +/// NOTE: A lot of code assumes that the two dimensions are equal, so we make it +/// explicit here. +/// +/// NOTE: It is highly unlikely that a value greater than 5 will work, as many +/// frontend optimizations rely on being able to pack chunk horizontal +/// dimensions into 5 bits each. +pub const TERRAIN_CHUNK_BLOCKS_LG: u32 = 5; + impl RectVolSize for TerrainChunkSize { - const RECT_SIZE: Vec2 = Vec2 { x: 32, y: 32 }; + const RECT_SIZE: Vec2 = Vec2 { + x: (1 << TERRAIN_CHUNK_BLOCKS_LG), + y: (1 << TERRAIN_CHUNK_BLOCKS_LG), + }; } // TerrainChunkMeta @@ -50,3 +69,140 @@ impl TerrainChunkMeta { pub type TerrainChunk = chonk::Chonk; pub type TerrainGrid = VolGrid2d; + +// Terrain helper functions used across multiple crates. + +/// Computes the position Vec2 of a SimChunk from an index, where the index was +/// generated by uniform_noise. +/// +/// NOTE: Dimensions obey constraints on [map::MapConfig::map_size_lg]. +#[inline(always)] +pub fn uniform_idx_as_vec2(map_size_lg: MapSizeLg, idx: usize) -> Vec2 { + let x_mask = (1 << map_size_lg.vec().x) - 1; + Vec2::new((idx & x_mask) as i32, (idx >> map_size_lg.vec().x) as i32) +} + +/// Computes the index of a Vec2 of a SimChunk from a position, where the index +/// is generated by uniform_noise. NOTE: Both components of idx should be +/// in-bounds! +#[inline(always)] +pub fn vec2_as_uniform_idx(map_size_lg: MapSizeLg, idx: Vec2) -> usize { + ((idx.y as usize) << map_size_lg.vec().x) | idx.x as usize +} + +// NOTE: want to keep this such that the chunk index is in ascending order! +pub const NEIGHBOR_DELTA: [(i32, i32); 8] = [ + (-1, -1), + (0, -1), + (1, -1), + (-1, 0), + (1, 0), + (-1, 1), + (0, 1), + (1, 1), +]; + +/// Iterate through all cells adjacent to a chunk. +#[inline(always)] +pub fn neighbors(map_size_lg: MapSizeLg, posi: usize) -> impl Clone + Iterator { + let pos = uniform_idx_as_vec2(map_size_lg, posi); + let world_size = map_size_lg.chunks(); + NEIGHBOR_DELTA + .iter() + .map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y)) + .filter(move |pos| { + pos.x >= 0 && pos.y >= 0 && pos.x < world_size.x as i32 && pos.y < world_size.y as i32 + }) + .map(move |pos| vec2_as_uniform_idx(map_size_lg, pos)) +} + +pub fn river_spline_coeffs( + // _sim: &WorldSim, + chunk_pos: Vec2, + spline_derivative: Vec2, + downhill_pos: Vec2, +) -> Vec3> { + let dxy = downhill_pos - chunk_pos; + // Since all splines have been precomputed, we don't have to do that much work + // to evaluate the spline. The spline is just ax^2 + bx + c = 0, where + // + // a = dxy - chunk.river.spline_derivative + // b = chunk.river.spline_derivative + // c = chunk_pos + let spline_derivative = spline_derivative.map(|e| e as f64); + Vec3::new(dxy - spline_derivative, spline_derivative, chunk_pos) +} + +/// Find the nearest point from a quadratic spline to this point (in terms of t, +/// the "distance along the curve" by which our spline is parameterized). Note +/// that if t < 0.0 or t >= 1.0, we probably shouldn't be considered "on the +/// curve"... hopefully this works out okay and gives us what we want (a +/// river that extends outwards tangent to a quadratic curve, with width +/// configured by distance along the line). +#[allow(clippy::let_and_return)] // TODO: Pending review in #587 +#[allow(clippy::many_single_char_names)] +pub fn quadratic_nearest_point( + spline: &Vec3>, + point: Vec2, +) -> Option<(f64, Vec2, f64)> { + let a = spline.z.x; + let b = spline.y.x; + let c = spline.x.x; + let d = point.x; + let e = spline.z.y; + let f = spline.y.y; + let g = spline.x.y; + let h = point.y; + // This is equivalent to solving the following cubic equation (derivation is a + // bit annoying): + // + // A = 2(c^2 + g^2) + // B = 3(b * c + g * f) + // C = ((a - d) * 2 * c + b^2 + (e - h) * 2 * g + f^2) + // D = ((a - d) * b + (e - h) * f) + // + // Ax³ + Bx² + Cx + D = 0 + // + // Once solved, this yield up to three possible values for t (reflecting minimal + // and maximal values). We should choose the minimal such real value with t + // between 0.0 and 1.0. If we fall outside those bounds, then we are + // outside the spline and return None. + let a_ = (c * c + g * g) * 2.0; + let b_ = (b * c + g * f) * 3.0; + let a_d = a - d; + let e_h = e - h; + let c_ = a_d * c * 2.0 + b * b + e_h * g * 2.0 + f * f; + let d_ = a_d * b + e_h * f; + let roots = find_roots_cubic(a_, b_, c_, d_); + let roots = roots.as_ref(); + + let min_root = roots + .iter() + .copied() + .filter_map(|root| { + let river_point = spline.x * root * root + spline.y * root + spline.z; + let river_zero = spline.z; + let river_one = spline.x + spline.y + spline.z; + if root > 0.0 && root < 1.0 { + Some((root, river_point)) + } else if river_point.distance_squared(river_zero) < 0.5 { + Some((root, /*river_point*/ river_zero)) + } else if river_point.distance_squared(river_one) < 0.5 { + Some((root, /*river_point*/ river_one)) + } else { + None + } + }) + .map(|(root, river_point)| { + let river_distance = river_point.distance_squared(point); + (root, river_point, river_distance) + }) + // In the (unlikely?) case that distances are equal, prefer the earliest point along the + // river. + .min_by(|&(ap, _, a), &(bp, _, b)| { + (a, ap < 0.0 || ap > 1.0, ap) + .partial_cmp(&(b, bp < 0.0 || bp > 1.0, bp)) + .unwrap() + }); + min_root +} diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 25501a4ac2..abe9168f4f 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -1,6 +1,7 @@ use super::BlockKind; use crate::{ assets::{self, Asset}, + make_case_elim, vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol}, volumes::dyna::{Dyna, DynaError}, }; @@ -9,25 +10,29 @@ use serde::Deserialize; use std::{fs::File, io::BufReader, sync::Arc}; use vek::*; -#[derive(Copy, Clone, PartialEq)] -pub enum StructureBlock { - None, - Grass, - TemperateLeaves, - PineLeaves, - Acacia, - Mangrove, - PalmLeavesInner, - PalmLeavesOuter, - Water, - GreenSludge, - Fruit, - Coconut, - Chest, - Hollow, - Liana, - Normal(Rgb), -} +make_case_elim!( + structure_block, + #[derive(Copy, Clone, PartialEq)] + #[repr(u32)] + pub enum StructureBlock { + None = 0, + Grass = 1, + TemperateLeaves = 2, + PineLeaves = 3, + Acacia = 4, + Mangrove = 5, + PalmLeavesInner = 6, + PalmLeavesOuter = 7, + Water = 8, + GreenSludge = 9, + Fruit = 10, + Coconut = 11, + Chest = 12, + Hollow = 13, + Liana = 14, + Normal(color: Rgb) = 15, + } +); impl Vox for StructureBlock { fn empty() -> Self { StructureBlock::None } diff --git a/common/src/typed.rs b/common/src/typed.rs new file mode 100644 index 0000000000..1dbf98f069 --- /dev/null +++ b/common/src/typed.rs @@ -0,0 +1,243 @@ +use core::marker::PhantomData; + +pub trait SubContext { + fn sub_context(self) -> Context; +} + +impl SubContext for Context { + fn sub_context(self) -> Context { self } +} + +impl SubContext for (Head, Tail) { + fn sub_context(self) -> Tail { self.1 } +} + +pub trait Typed { + fn reduce(self, context: Context) -> (Type, S); +} + +pub struct Pure(pub T); + +impl, T, S> Typed, S> for T { + fn reduce(self, context: Context) -> (Pure, S) { (Pure(self), context.sub_context()) } +} + +/// A lazy pattern match reified as a Rust type. +/// +/// `expr` is the expression being matched on, generally of some enum type `Ty`. +/// +/// `case` represents the pattern match--it will generally be a structure with +/// one field per constructor in `Ty`. The field should contain enough +/// information to run the match arm for that constructor, given the information +/// contained in the constructor arguments. +/// +/// `ty` represents the return type of the match expression. It does not carry +/// any runtime-relevant information, but is needed in order to simplify our +/// trait definitions. +/// +/// The intent is that you should not construct this structure directly, nor +/// should you define or construct the `Cases` structure directly. Instead, to +/// use this you are expected to wrap your enum declaration in a call to +/// [make_case_elim!], as follows: +/// +/// ``` +/// # #![feature(arbitrary_enum_discriminant)] +/// # #[macro_use] extern crate veloren_common; +/// +/// veloren_common::make_case_elim!( +/// my_type_module, +/// #[repr(u32)] +/// #[derive(Clone,Copy)] +/// pub enum MyType { +/// Constr1 = 0, +/// Constr2(arg : u8) = 1, +/// /* ..., */ +/// } +/// ); +/// ``` +/// +/// This macro automatically does a few things. First, it creates the `enum` +/// type `MyType` in the current scope, as expected. Second, it creates a +/// module named `my_type_module` in the current scope, into which it dumps a +/// few things. In this case: +/// +/// ``` +/// # #![feature(arbitrary_enum_discriminant)] +/// # #[macro_use] extern crate veloren_common; +/// +/// #[repr(u32)] +/// #[derive(Clone, Copy)] +/// pub enum MyType { +/// Constr1 = 0, +/// Constr2(u8) = 1, +/// /* ..., */ +/// } +/// +/// # #[allow(non_snake_case)] +/// # #[allow(dead_code)] +/// mod my_type_module { +/// use ::serde::{Deserialize, Serialize}; +/// +/// /// The number of variants in this enum. +/// pub const NUM_VARIANTS: usize = 2; +/// +/// /// An array of all the variant indices (in theory, this can be used by this or other +/// /// macros in order to easily build up things like uniform random samplers). +/// pub const ALL_INDICES: [u32; NUM_VARIANTS] = [0, 1]; +/// +/// /// A convenience trait used to store a different type for each constructor in this +/// /// pattern. +/// pub trait PackedElim { +/// type Constr1; +/// type Constr2; +/// } +/// +/// /// The actual *cases.* If you think of pattern match arms as being closures that accept +/// /// the cconstructor types as arguments, you can think of this structure as somehow +/// /// representing just the data *owned* by the closure. This is also what you will +/// /// generally store in your ron file--it has a field for each constructor of your enum, +/// /// with the types of all the fields specified by the implementation of [PackedElim] for +/// /// the [Elim] argument. Each field has the same name as the constructor it represents. +/// #[derive(Serialize, Deserialize)] +/// pub struct Cases { +/// pub Constr1: Elim::Constr1, +/// pub Constr2: Elim::Constr2, +/// } +/// +/// /// Finally, because it represents by an overwhelming margin the most common usecase, we +/// /// predefine a particular pattern matching strategy--"pure"--where every arm holds data of +/// /// the exact same type, T. +/// impl PackedElim for veloren_common::typed::Pure { +/// type Constr1 = T; +/// type Constr2 = T; +/// } +/// +/// /// Because PureCases is so convenient, we have an alias for it. Thus, in order to +/// /// represent a pattern match on an argument that returns a constant of type (u8,u8,u8) for +/// /// each arm, you'd use the type `PureCases<(u8, u8, u8)>`. +/// pub type PureCases = Cases>; +/// } +/// ``` +/// +/// Finally, a useful implementation of the [Typed] trait completes this story, +/// providing a way to evaluate this lazy math statement within Rust. +/// Unfortunately, [Typed] is quite complicated, and this story is still being +/// fully evaluated, so showing teh type may not be that elucidating. +/// Instead, we'll just present the method you can use most easily to pattern +/// match using the PureCases pattern we mentioned earlier: +/// +/// pub fn elim_case_pure<'a, Type>(&'a self, cases: &'a $mod::PureCases) +/// -> &'a Type +/// +/// If self is expression of your defined enum type, and match data defined by +/// PureCases, this evaluates the pattern match on self and returns the matched +/// case. +/// +/// To see how this is used in more detail, check out +/// `common/src/body/humanoid.rs`; it is also used extensively in the world +/// repository. +/// +/// --- +/// +/// Limitations: +/// +/// Unfortunately, due to restrictions on procedural macros, we currently always +/// require the types defined to #[repr(inttype)] as you can see above. There +/// are also some other current limitations that we hopefully will be able to +/// lift at some point; struct variants are not yet supported, and neither +/// attributes on fields. +#[fundamental] +pub struct ElimCase { + pub expr: Expr, + pub cases: Cases, + pub ty: PhantomData, +} + +#[macro_export] +macro_rules! as_item { + ($i:item) => { + $i + }; +} + +#[macro_export] +macro_rules! make_case_elim { + ($mod:ident, $( #[$ty_attr:meta] )* $vis:vis enum $ty:ident { + $( $constr:ident $( ( $( $arg_name:ident : $arg_ty:ty ),* ) )? = $index:expr ),* $(,)? + }) => { + $crate::as_item! { + $( #[$ty_attr] )* + $vis enum $ty { + $( $constr $( ($( $arg_ty, )*) )? = $index, )* + } + } + + #[allow(non_snake_case)] + #[allow(dead_code)] + $vis mod $mod { + use ::serde::{Deserialize, Serialize}; + + pub const NUM_VARIANTS: usize = 0 $( + { let _ = $index; 1 } )*; + + pub const ALL_INDICES: [u32; NUM_VARIANTS] = [ $( $index, )* ]; + + pub trait PackedElim { + $( type $constr; )* + } + + #[derive(Serialize, Deserialize)] + pub struct Cases { + $( pub $constr : Elim::$constr, )* + } + + impl PackedElim for $crate::typed::Pure { + $( type $constr = T; )* + } + + pub type PureCases = Cases<$crate::typed::Pure>; + } + + #[allow(unused_parens)] + impl<'a, Elim: $mod::PackedElim, Context, Type, S> + $crate::typed::Typed for $crate::typed::ElimCase<&'a $ty, &'a $mod::Cases, Type> + where + $( &'a Elim::$constr: $crate::typed::Typed<($( ($( &'a $arg_ty, )*), )? Context), Type, S>, )* + { + fn reduce(self, context: Context) -> (Type, S) + { + let Self { expr, cases, .. } = self; + match expr { + $( $ty::$constr $( ($( $arg_name, )*) )? => + <_ as $crate::typed::Typed<_, Type, _>>::reduce( + &cases.$constr, + ($( ($( $arg_name, )*), )? context), + ), + )* + } + } + } + + impl $ty { + pub fn elim_case<'a, Elim: $mod::PackedElim, Context, S, Type>(&'a self, cases: &'a $mod::Cases, context: Context) -> + (Type, S) + where + $crate::typed::ElimCase<&'a $ty, &'a $mod::Cases, Type> : $crate::typed::Typed, + { + use $crate::typed::Typed; + + let case = $crate::typed::ElimCase { + expr: self, + cases, + ty: ::core::marker::PhantomData, + }; + case.reduce(context) + } + + pub fn elim_case_pure<'a, Type>(&'a self, cases: &'a $mod::PureCases) -> &'a Type + { + let ($crate::typed::Pure(expr), ()) = self.elim_case(cases, ()); + expr + } + } + } +} diff --git a/common/src/util/color.rs b/common/src/util/color.rs index 72a2eca0f2..61a166693e 100644 --- a/common/src/util/color.rs +++ b/common/src/util/color.rs @@ -98,13 +98,20 @@ pub fn hsv_to_rgb(hsv: Vec3) -> Rgb { Rgb::new(r + m, g + m, b + m) } +/// Convert linear rgb to CIEXYZ +#[inline(always)] +pub fn rgb_to_xyz(rgb: Rgb) -> Vec3 { + // XYZ + Mat3::new( + 0.4124, 0.3576, 0.1805, 0.2126, 0.7152, 0.0722, 0.0193, 0.1192, 0.9504, + ) * Vec3::from(rgb) +} + /// Convert linear rgb to CIExyY #[inline(always)] pub fn rgb_to_xyy(rgb: Rgb) -> Vec3 { // XYZ - let xyz = Mat3::new( - 0.4124, 0.3576, 0.1805, 0.2126, 0.7152, 0.0722, 0.0193, 0.1192, 0.9504, - ) * Vec3::from(rgb); + let xyz = rgb_to_xyz(rgb); let sum = xyz.sum(); Vec3::new(xyz.x / sum, xyz.y / sum, xyz.y) diff --git a/common/src/vol.rs b/common/src/vol.rs index 210779c8d6..2313eea2e7 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/network/src/message.rs b/network/src/message.rs index 5238f6791a..d5ab754a26 100644 --- a/network/src/message.rs +++ b/network/src/message.rs @@ -99,37 +99,17 @@ pub(crate) fn partial_eq_io_error(first: &io::Error, second: &io::Error) -> bool } pub(crate) fn partial_eq_bincode(first: &bincode::ErrorKind, second: &bincode::ErrorKind) -> bool { + use bincode::ErrorKind::*; match *first { - bincode::ErrorKind::Io(ref f) => match *second { - bincode::ErrorKind::Io(ref s) => partial_eq_io_error(f, s), - _ => false, - }, - bincode::ErrorKind::InvalidUtf8Encoding(f) => match *second { - bincode::ErrorKind::InvalidUtf8Encoding(s) => f == s, - _ => false, - }, - bincode::ErrorKind::InvalidBoolEncoding(f) => match *second { - bincode::ErrorKind::InvalidBoolEncoding(s) => f == s, - _ => false, - }, - bincode::ErrorKind::InvalidCharEncoding => { - matches!(*second, bincode::ErrorKind::InvalidCharEncoding) - }, - bincode::ErrorKind::InvalidTagEncoding(f) => match *second { - bincode::ErrorKind::InvalidTagEncoding(s) => f == s, - _ => false, - }, - bincode::ErrorKind::DeserializeAnyNotSupported => { - matches!(*second, bincode::ErrorKind::DeserializeAnyNotSupported) - }, - bincode::ErrorKind::SizeLimit => matches!(*second, bincode::ErrorKind::SizeLimit), - bincode::ErrorKind::SequenceMustHaveLength => { - matches!(*second, bincode::ErrorKind::SequenceMustHaveLength) - }, - bincode::ErrorKind::Custom(ref f) => match *second { - bincode::ErrorKind::Custom(ref s) => f == s, - _ => false, - }, + Io(ref f) => matches!(*second, Io(ref s) if partial_eq_io_error(f, s)), + InvalidUtf8Encoding(f) => matches!(*second, InvalidUtf8Encoding(s) if f == s), + InvalidBoolEncoding(f) => matches!(*second, InvalidBoolEncoding(s) if f == s), + InvalidCharEncoding => matches!(*second, InvalidCharEncoding), + InvalidTagEncoding(f) => matches!(*second, InvalidTagEncoding(s) if f == s), + DeserializeAnyNotSupported => matches!(*second, DeserializeAnyNotSupported), + SizeLimit => matches!(*second, SizeLimit), + SequenceMustHaveLength => matches!(*second, SequenceMustHaveLength), + Custom(ref f) => matches!(*second, Custom(ref s) if f == s), } } diff --git a/server/Cargo.toml b/server/Cargo.toml index 25fe6ba4db..e0a6493d08 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -17,7 +17,7 @@ specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "spec tracing = "0.1" specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" } -vek = "0.11.0" +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } uvth = "3.1.1" futures-util = "0.3" futures-executor = "0.3" diff --git a/server/src/chunk_generator.rs b/server/src/chunk_generator.rs index 3e769956da..b69d597c12 100644 --- a/server/src/chunk_generator.rs +++ b/server/src/chunk_generator.rs @@ -1,5 +1,5 @@ #[cfg(not(feature = "worldgen"))] -use crate::test_world::World; +use crate::test_world::{IndexOwned, World}; use common::{generation::ChunkSupplement, terrain::TerrainChunk}; use crossbeam::channel; use hashbrown::{hash_map::Entry, HashMap}; @@ -9,11 +9,12 @@ use std::sync::{ Arc, }; use vek::*; -#[cfg(feature = "worldgen")] use world::World; +#[cfg(feature = "worldgen")] +use world::{IndexOwned, World}; type ChunkGenResult = ( Vec2, - Result<(TerrainChunk, ChunkSupplement), EcsEntity>, + Result<(TerrainChunk, ChunkSupplement), Option>, ); pub struct ChunkGenerator { @@ -34,10 +35,11 @@ impl ChunkGenerator { pub fn generate_chunk( &mut self, - entity: EcsEntity, + entity: Option, key: Vec2, thread_pool: &mut uvth::ThreadPool, world: Arc, + index: IndexOwned, ) { let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) { v @@ -48,8 +50,9 @@ impl ChunkGenerator { v.insert(Arc::clone(&cancel)); let chunk_tx = self.chunk_tx.clone(); thread_pool.execute(move || { + let index = index.as_index_ref(); let payload = world - .generate_chunk(key, || cancel.load(Ordering::Relaxed)) + .generate_chunk(index, key, || cancel.load(Ordering::Relaxed)) .map_err(|_| entity); let _ = chunk_tx.send((key, payload)); }); @@ -74,4 +77,10 @@ impl ChunkGenerator { cancel.store(true, Ordering::Relaxed); } } + + pub fn cancel_all(&mut self) { + self.pending_chunks.drain().for_each(|(_, cancel)| { + cancel.store(true, Ordering::Relaxed); + }); + } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7a3da0ba47..2d15bc6921 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1473,7 +1473,7 @@ fn handle_debug_column( let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32); let chunk = sim.get(chunk_pos)?; - let col = sampler.get((wpos, server.world.index()))?; + let col = sampler.get((wpos, server.index.as_index_ref()))?; let downhill = chunk.downhill; let river = &chunk.river; let flux = chunk.flux; diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 5e910452c9..206136aadc 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -119,7 +119,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { .create_object(Pos(pos), comp::object::Body::CampfireLit) .with(LightEmitter { col: Rgb::new(1.0, 0.65, 0.2), - strength: 2.0, + strength: 5.0, flicker: 1.0, animated: true, }) diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index b09f4c9f0d..f03fa99906 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -33,6 +33,14 @@ pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) { /// other players. If the entity that killed it had stats, then give it exp for /// the kill. Experience given is equal to the level of the entity that was /// killed times 10. +// NOTE: Clippy incorrectly warns about a needless collect here because it does not +// understand that the pet count (which is computed during the first iteration over the +// members in range) is actually used by the second iteration over the members in range; +// since we have no way of knowing the pet count before the first loop finishes, we +// definitely need at least two loops. Then (currently) our only options are to store +// the member list in temporary space (e.g. by collecting to a vector), or to repeat +// the loop; but repeating the loop would currently be very inefficient since it has to +// rescan every entity on the server again. #[allow(clippy::needless_collect)] pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSource) { let state = server.state_mut(); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index de201fd6fa..a646b561f9 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -337,6 +337,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .and_then(|ldt| slot::loadout_remove(slot, ldt)), }; + // FIXME: We should really require the drop and write to be atomic! if let (Some(item), Some(pos)) = (item, state.ecs().read_storage::().get(entity)) { @@ -363,6 +364,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let recipe_book = default_recipe_book(); let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok()); + // FIXME: We should really require the drop and write to be atomic! if craft_result.is_some() { let _ = state.ecs().write_storage().insert( entity, diff --git a/server/src/lib.rs b/server/src/lib.rs index 241c9588f8..aef6fe1dbb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -33,7 +33,7 @@ use common::{ cmd::ChatCommand, comp::{self, ChatType}, event::{EventBus, ServerEvent}, - msg::{ClientState, ServerInfo, ServerMsg}, + msg::{server::WorldMapMsg, ClientState, ServerInfo, ServerMsg}, outcome::Outcome, recipe::default_recipe_book, state::{State, TimeOfDay}, @@ -55,15 +55,15 @@ use std::{ time::{Duration, Instant}, }; #[cfg(not(feature = "worldgen"))] -use test_world::{World, WORLD_SIZE}; +use test_world::{IndexOwned, World}; use tracing::{debug, error, info, warn}; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; #[cfg(feature = "worldgen")] use world::{ civ::SiteKind, - sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, WORLD_SIZE}, - World, + sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, + IndexOwned, World, }; #[macro_use] extern crate diesel; @@ -82,7 +82,8 @@ pub struct Tick(u64); pub struct Server { state: State, world: Arc, - map: Vec, + index: IndexOwned, + map: WorldMapMsg, network: Network, @@ -173,7 +174,7 @@ impl Server { state.ecs_mut().insert(AliasValidator::new(banned_words)); #[cfg(feature = "worldgen")] - let world = World::generate(settings.world_seed, WorldOpts { + let (world, index) = World::generate(settings.world_seed, WorldOpts { seed_elements: true, world_file: if let Some(ref opts) = settings.map_file { opts.clone() @@ -184,21 +185,27 @@ impl Server { ..WorldOpts::default() }); #[cfg(feature = "worldgen")] - let map = world.get_map_data(); + let map = world.get_map_data(index.as_index_ref()); #[cfg(not(feature = "worldgen"))] - let world = World::generate(settings.world_seed); + let (world, index) = World::generate(settings.world_seed); #[cfg(not(feature = "worldgen"))] - let map = vec![0]; + let map = WorldMapMsg { + dimensions: Vec2::new(1, 1), + max_height: 1.0, + rgba: vec![0], + horizons: [(vec![0], vec![0]), (vec![0], vec![0])], + }; #[cfg(feature = "worldgen")] let spawn_point = { + let index = index.as_index_ref(); // NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops, // but are needed to be explicit about casting (and to make the compiler stop // complaining) // spawn in the chunk, that is in the middle of the world - let center_chunk: Vec2 = WORLD_SIZE.map(|e| e as i32) / 2; + let center_chunk: Vec2 = world.sim().map_size_lg().chunks().map(i32::from) / 2; // Find a town to spawn in that's close to the centre of the world let spawn_chunk = world @@ -219,11 +226,11 @@ impl Server { // get a z cache for the collumn in which we want to spawn let mut block_sampler = world.sample_blocks(); let z_cache = block_sampler - .get_z_cache(spawn_location, world.index()) + .get_z_cache(spawn_location, index) .expect(&format!("no z_cache found for chunk: {}", spawn_chunk)); // get the minimum and maximum z values at which there could be soild blocks - let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, world.index()); + let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, index); // round range outwards, so no potential air block is missed let min_z = min_z.floor() as i32; let max_z = max_z.ceil() as i32; @@ -239,7 +246,7 @@ impl Server { Vec3::new(spawn_location.x, spawn_location.y, *z), Some(&z_cache), false, - world.index(), + index, ) .map(|b| b.is_air()) .unwrap_or(false) @@ -283,6 +290,7 @@ impl Server { let this = Self { state, world: Arc::new(world), + index, map, network, @@ -489,6 +497,42 @@ impl Server { }, }); + { + // Check for new chunks; cancel and regenerate all chunks if the asset has been + // reloaded. Note that all of these assignments are no-ops, so the + // only work we do here on the fast path is perform a relaxed read on an atomic. + // boolean. + let index = &mut self.index; + let thread_pool = &mut self.thread_pool; + let world = &mut self.world; + let ecs = self.state.ecs_mut(); + + index.reload_colors_if_changed(|index| { + let mut chunk_generator = ecs.write_resource::(); + let client = ecs.read_storage::(); + let mut terrain = ecs.write_resource::(); + + // Cancel all pending chunks. + chunk_generator.cancel_all(); + + if client.is_empty() { + // No cilents, so just clear all terrain. + terrain.clear(); + } else { + // There's at leasat one client, so regenerate all chunks. + terrain.iter().for_each(|(pos, _)| { + chunk_generator.generate_chunk( + None, + pos, + thread_pool, + world.clone(), + index.clone(), + ); + }); + } + }); + } + let end_of_server_tick = Instant::now(); // 8) Update Metrics @@ -700,7 +744,7 @@ impl Server { server_info: self.get_server_info(), time_of_day: *self.state.ecs().read_resource(), max_group_size: self.settings().max_player_group_size, - world_map: (WORLD_SIZE.map(|e| e as u32), self.map.clone()), + world_map: self.map.clone(), recipe_book: (&*default_recipe_book()).clone(), }); @@ -723,7 +767,13 @@ impl Server { self.state .ecs() .write_resource::() - .generate_chunk(entity, key, &mut self.thread_pool, self.world.clone()); + .generate_chunk( + Some(entity), + key, + &mut self.thread_pool, + self.world.clone(), + self.index.clone(), + ); } fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) { diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index e2d8a8c15f..ac5b8e098f 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -206,7 +206,6 @@ impl StateExt for State { } } - #[allow(clippy::map_identity)] // TODO: Pending review in #587 fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { let (body, stats, inventory, loadout) = components; // Make sure physics are accepted. @@ -215,7 +214,6 @@ impl StateExt for State { // Notify clients of a player list update let client_uid = self .read_component_cloned::(entity) - .map(|u| u) .expect("Client doesn't have a Uid!!!"); self.notify_registered_clients(ServerMsg::PlayerListUpdate( diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 739d1988b1..65277a7d9a 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -60,7 +60,7 @@ impl<'a> System<'a> for Sys { 'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() { let (chunk, supplement) = match res { Ok((chunk, supplement)) => (chunk, supplement), - Err(entity) => { + Err(Some(entity)) => { if let Some(client) = clients.get_mut(entity) { client.notify(ServerMsg::TerrainChunkUpdate { key, @@ -69,6 +69,9 @@ impl<'a> System<'a> for Sys { } continue 'insert_terrain_chunks; }, + Err(None) => { + continue 'insert_terrain_chunks; + }, }; // Send the chunk to all nearby players. for (view_distance, pos, client) in (&players, &positions, &mut clients) diff --git a/server/src/test_world.rs b/server/src/test_world.rs index b95fabfb61..4df254bf21 100644 --- a/server/src/test_world.rs +++ b/server/src/test_world.rs @@ -1,23 +1,49 @@ use common::{ generation::{ChunkSupplement, EntityInfo}, - terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, + terrain::{Block, BlockKind, MapSizeLg, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, vol::{ReadVol, RectVolSize, Vox, WriteVol}, }; use rand::{prelude::*, rngs::SmallRng}; use std::time::Duration; use vek::*; -pub const WORLD_SIZE: Vec2 = Vec2 { x: 1, y: 1 }; +const DEFAULT_WORLD_CHUNKS_LG: MapSizeLg = + if let Ok(map_size_lg) = MapSizeLg::new(Vec2 { x: 1, y: 1 }) { + map_size_lg + } else { + panic!("Default world chunk size does not satisfy required invariants."); + }; pub struct World; +#[derive(Clone)] +pub struct IndexOwned; + +#[derive(Clone, Copy)] +pub struct IndexRef<'a>(&'a IndexOwned); + +impl IndexOwned { + pub fn reload_colors_if_changed( + &mut self, + _reload: impl FnOnce(&mut Self) -> R, + ) -> Option { + None + } + + pub fn as_index_ref(&self) -> IndexRef { IndexRef(self) } +} + impl World { - pub fn generate(_seed: u32) -> Self { Self } + pub fn generate(_seed: u32) -> (Self, IndexOwned) { (Self, IndexOwned) } pub fn tick(&self, dt: Duration) {} + #[inline(always)] + pub const fn map_size_lg(&self) -> MapSizeLg { DEFAULT_WORLD_CHUNKS_LG } + pub fn generate_chunk( &self, + _index: IndexRef, chunk_pos: Vec2, _should_continue: impl FnMut() -> bool, ) -> Result<(TerrainChunk, ChunkSupplement), ()> { diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index a5d55b2c5f..459cd9657d 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"] hot-anim = ["anim/use-dyn-lib"] @@ -25,6 +25,7 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features = # Graphics gfx = "0.18.2" gfx_device_gl = { version = "0.16.2", optional = true } +gfx_gl = { version = "0.6.1", optional = true } old_school_gfx_glutin_ext = "0.24" glutin = "0.24.1" winit = { version = "0.22.2", features = ["serde"] } @@ -37,7 +38,7 @@ specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818 specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" } # Mathematics -vek = { version = "0.11.0", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } # Controller gilrs = { version = "0.7", features = ["serde"] } @@ -49,11 +50,11 @@ server = { package = "veloren-server", path = "../server", optional = true } glsl-include = "0.3.1" failure = "0.1.6" dot_vox = "4.0" -image = { version = "0.22.5", default-features = false, features = ["ico", "png"] } +image = { version = "0.23.8", default-features = false, features = ["ico", "png"] } serde = "1.0" serde_derive = "1.0" ron = { version = "0.6", default-features = false } -guillotiere = { git = "https://github.com/Imberflur/guillotiere" } +guillotiere = "0.5.2" msgbox = { git = "https://github.com/bekker/msgbox-rs.git", default-features = false, rev = "68fe39a", optional = true } directories-next = "1.0.1" num = "0.2" @@ -68,6 +69,7 @@ chrono = "0.4.9" bincode = "1.2" deunicode = "1.0" uvth = "3.1.1" +# vec_map = { version = "0.8.2" } const-tweaker = { version = "0.3.1", optional = true } itertools = "0.9.0" diff --git a/voxygen/benches/meshing_benchmark.rs b/voxygen/benches/meshing_benchmark.rs index 45b7a39403..74085505d2 100644 --- a/voxygen/benches/meshing_benchmark.rs +++ b/voxygen/benches/meshing_benchmark.rs @@ -15,7 +15,7 @@ const GEN_SIZE: i32 = 4; pub fn criterion_benchmark(c: &mut Criterion) { // Generate chunks here to test let mut terrain = TerrainGrid::new().unwrap(); - let world = World::generate(42, sim::WorldOpts { + let (world, index) = World::generate(42, sim::WorldOpts { // NOTE: If this gets too expensive, we can turn it off. // TODO: Consider an option to turn off all erosion as well, or even provide altitude // directly with a closure. @@ -23,10 +23,11 @@ pub fn criterion_benchmark(c: &mut Criterion) { world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()), ..Default::default() }); + let index = index.as_index_ref(); (0..GEN_SIZE) .flat_map(|x| (0..GEN_SIZE).map(move |y| Vec2::new(x, y))) .map(|offset| offset + CENTER) - .map(|pos| (pos, world.generate_chunk(pos, || false).unwrap())) + .map(|pos| (pos, world.generate_chunk(index, pos, || false).unwrap())) .for_each(|(key, chunk)| { terrain.insert(key, Arc::new(chunk.0)); }); @@ -132,7 +133,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/clippy.toml b/voxygen/clippy.toml new file mode 100644 index 0000000000..fee00d7951 --- /dev/null +++ b/voxygen/clippy.toml @@ -0,0 +1,2 @@ +# Because 7 is just very low, it corresponds to a 6-argument method. +too-many-arguments-threshold = 10 diff --git a/voxygen/examples/character_renderer.rs b/voxygen/examples/character_renderer.rs index 70cfe2a725..aa02b414f4 100644 --- a/voxygen/examples/character_renderer.rs +++ b/voxygen/examples/character_renderer.rs @@ -22,16 +22,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, - ) - .unwrap(); + let mut renderer = + render::Renderer::new(device, factory, color_view, depth_view, Default::default()).unwrap(); // Create character let body = comp::humanoid::Body::random(); diff --git a/voxygen/src/anim/Cargo.toml b/voxygen/src/anim/Cargo.toml index 506e6f49b1..45b293d874 100644 --- a/voxygen/src/anim/Cargo.toml +++ b/voxygen/src/anim/Cargo.toml @@ -17,7 +17,7 @@ be-dyn-lib = [] default = ["be-dyn-lib"] [dependencies] -vek = { version = "0.11.2", features = ["serde"] } +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } common = { package = "veloren-common", path = "../../../common" } libloading = { version = "0.6.2", optional = true } notify = { version = "5.0.0-pre.2", optional = true } diff --git a/voxygen/src/anim/src/biped_large/alpha.rs b/voxygen/src/anim/src/biped_large/alpha.rs index 7646ed3641..7ed3c6fe8c 100644 --- a/voxygen/src/anim/src/biped_large/alpha.rs +++ b/voxygen/src/anim/src/biped_large/alpha.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -61,150 +63,152 @@ impl Animation for AlphaAnimation { let short = (anim_time as f32 * lab as f32 * 16.0).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(slower * 1.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = Quaternion::rotation_z(slower * 1.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.02; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.0); next.main.scale = Vec3::one() * 1.02; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_l.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_l.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_r.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_r.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_r.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1, ); - next.upper_torso.ori = Quaternion::rotation_z(slower * -1.2) * Quaternion::rotation_x(-0.3); + next.upper_torso.orientation = + Quaternion::rotation_z(slower * -1.2) * Quaternion::rotation_x(-0.3); next.upper_torso.scale = Vec3::one(); - next.control.offset = Vec3::new(7.0, 9.0, -10.0); - next.control.ori = Quaternion::rotation_x(slowersmooth * 0.35) + next.control.position = Vec3::new(7.0, 9.0, -10.0); + next.control.orientation = Quaternion::rotation_x(slowersmooth * 0.35) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(slowersmooth * -0.5 + slower * -0.5); next.control.scale = Vec3::one(); if velocity < 0.5 { - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); + next.lower_torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); - next.jaw.ori = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); + next.jaw.orientation = Quaternion::rotation_z(0.0); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 8.0; } else { - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = + next.lower_torso.orientation = Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); next.lower_torso.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothoril * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_x(0.5 + footrotl * -0.16) + next.shoulder_l.orientation = Quaternion::rotation_x(0.5 + footrotl * -0.16) * Quaternion::rotation_y(0.1) * Quaternion::rotation_z(footrotl * 0.1); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothorir * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_x(0.5 + footrotr * -0.16) + next.shoulder_r.orientation = Quaternion::rotation_x(0.5 + footrotr * -0.16) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(footrotr * -0.1); next.shoulder_r.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); next.torso.scale = Vec3::one() / 8.0; } diff --git a/voxygen/src/anim/src/biped_large/idle.rs b/voxygen/src/anim/src/biped_large/idle.rs index 8e9ca72cbc..dffe3d56ae 100644 --- a/voxygen/src/anim/src/biped_large/idle.rs +++ b/voxygen/src/anim/src/biped_large/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -39,122 +41,123 @@ impl Animation for IdleAnimation { let wave_slow = (anim_time as f32 * 0.8).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + torso * 0.2, ) * 1.02; - next.head.ori = Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); + next.head.orientation = + Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + torso * 0.5, ); - next.upper_torso.ori = Quaternion::rotation_z(0.0); + next.upper_torso.orientation = Quaternion::rotation_z(0.0); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1 + torso * 0.15, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.lower_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.09); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.09); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + torso * 0.0, ); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_z(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-5.0, -7.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-5.0, -7.0, 7.0); + next.main.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.6) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 8.0; next diff --git a/voxygen/src/anim/src/biped_large/jump.rs b/voxygen/src/anim/src/biped_large/jump.rs index 0b7725e4d6..037c2dfbd1 100644 --- a/voxygen/src/anim/src/biped_large/jump.rs +++ b/voxygen/src/anim/src/biped_large/jump.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct JumpAnimation; @@ -26,122 +28,122 @@ impl Animation for JumpAnimation { let wave_slow = (anim_time as f32 * 0.8).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + torso * 0.2, ) * 1.02; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + torso * 0.5, ); - next.upper_torso.ori = Quaternion::rotation_x(-0.3); + next.upper_torso.orientation = Quaternion::rotation_x(-0.3); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1 + torso * 0.15, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); + next.lower_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.2); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.09); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.09); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + torso * 0.0, ); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_z(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-5.0, -7.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-5.0, -7.0, 7.0); + next.main.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.6) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.5); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.5); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.5); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.5); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.8); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.8); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.8); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.8); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + torso * 0.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, -5.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.4); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, 5.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 8.0; next diff --git a/voxygen/src/anim/src/biped_large/mod.rs b/voxygen/src/anim/src/biped_large/mod.rs index 572f642a59..e812e0cd8b 100644 --- a/voxygen/src/anim/src/biped_large/mod.rs +++ b/voxygen/src/anim/src/biped_large/mod.rs @@ -10,103 +10,69 @@ pub use self::{ wield::WieldAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct BipedLargeSkeleton { - head: Bone, - jaw: Bone, - upper_torso: Bone, - lower_torso: Bone, - tail: Bone, - main: Bone, - second: Bone, - shoulder_l: Bone, - shoulder_r: Bone, - hand_l: Bone, - hand_r: Bone, - leg_l: Bone, - leg_r: Bone, - foot_l: Bone, - foot_r: Bone, - torso: Bone, - control: Bone, -} - -impl BipedLargeSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct BipedLargeSkeleton { + + head, + + jaw, + + upper_torso, + + lower_torso, + + tail, + + main, + + second, + + shoulder_l, + + shoulder_r, + + hand_l, + + hand_r, + + leg_l, + + leg_r, + + foot_l, + + foot_r, + torso, + control, +}); impl Skeleton for BipedLargeSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 15; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"biped_large_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let jaw_mat = self.jaw.compute_base_matrix(); - let upper_torso_mat = self.upper_torso.compute_base_matrix(); - let lower_torso_mat = self.lower_torso.compute_base_matrix(); - let tail_mat = self.tail.compute_base_matrix(); - let main_mat = self.main.compute_base_matrix(); - let second_mat = self.second.compute_base_matrix(); - let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); - let shoulder_r_mat = self.shoulder_r.compute_base_matrix(); - let hand_l_mat = self.hand_l.compute_base_matrix(); - let hand_r_mat = self.hand_r.compute_base_matrix(); - let leg_l_mat = self.leg_l.compute_base_matrix(); - let leg_r_mat = self.leg_r.compute_base_matrix(); - let torso_mat = self.torso.compute_base_matrix(); - let control_mat = self.control.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let upper_torso = Mat4::::from(self.upper_torso); - ( - [ - FigureBoneData::new(torso_mat * upper_torso_mat * self.head.compute_base_matrix()), - FigureBoneData::new( - torso_mat * upper_torso_mat * self.head.compute_base_matrix() * jaw_mat, - ), - FigureBoneData::new(torso_mat * upper_torso_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat * tail_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * main_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * second_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 * control_mat * hand_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * control_mat * hand_r_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat * leg_l_mat), - FigureBoneData::new(torso_mat * upper_torso_mat * lower_torso_mat * leg_r_mat), - FigureBoneData::new(self.foot_l.compute_base_matrix()), - FigureBoneData::new(self.foot_r.compute_base_matrix()), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + let torso_mat = base_mat * Mat4::::from(self.torso); + let upper_torso_mat = torso_mat * upper_torso; + let lower_torso_mat = upper_torso_mat * Mat4::::from(self.lower_torso); + let head_mat = upper_torso_mat * Mat4::::from(self.head); + let control_mat = upper_torso_mat * Mat4::::from(self.control); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.jaw.interpolate(&target.jaw, dt); - self.upper_torso.interpolate(&target.upper_torso, dt); - self.lower_torso.interpolate(&target.lower_torso, dt); - self.tail.interpolate(&target.tail, dt); - self.main.interpolate(&target.main, dt); - self.second.interpolate(&target.second, dt); - self.shoulder_l.interpolate(&target.shoulder_l, dt); - self.shoulder_r.interpolate(&target.shoulder_r, dt); - self.hand_l.interpolate(&target.hand_l, dt); - self.hand_r.interpolate(&target.hand_r, dt); - self.leg_l.interpolate(&target.leg_l, dt); - self.leg_r.interpolate(&target.leg_r, dt); - self.foot_l.interpolate(&target.foot_l, dt); - self.foot_r.interpolate(&target.foot_r, dt); - self.torso.interpolate(&target.torso, dt); - self.control.interpolate(&target.control, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_mat), + make_bone(head_mat * Mat4::::from(self.jaw)), + make_bone(upper_torso_mat), + make_bone(lower_torso_mat), + make_bone(lower_torso_mat * Mat4::::from(self.tail)), + make_bone(control_mat * Mat4::::from(self.main)), + make_bone(control_mat * Mat4::::from(self.second)), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_l)), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_r)), + make_bone(control_mat * Mat4::::from(self.hand_l)), + make_bone(control_mat * Mat4::::from(self.hand_r)), + make_bone(lower_torso_mat * Mat4::::from(self.leg_l)), + make_bone(lower_torso_mat * Mat4::::from(self.leg_r)), + make_bone(base_mat * Mat4::::from(self.foot_l)), + make_bone(base_mat * Mat4::::from(self.foot_r)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/biped_large/run.rs b/voxygen/src/anim/src/biped_large/run.rs index a7929510f9..ede6b30ebb 100644 --- a/voxygen/src/anim/src/biped_large/run.rs +++ b/voxygen/src/anim/src/biped_large/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -57,125 +59,127 @@ impl Animation for RunAnimation { let shortalt = (anim_time as f32 * lab as f32 * 16.0 + PI / 2.0).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = + Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + shortalt * -1.5, ); - next.upper_torso.ori = Quaternion::rotation_z(short * 0.18); + next.upper_torso.orientation = Quaternion::rotation_z(short * 0.18); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); + next.lower_torso.orientation = + Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_z(0.0); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 * 0.0); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 * 0.0); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_z(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); - next.main.offset = Vec3::new(-5.0, -7.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-5.0, -7.0, 7.0); + next.main.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.6) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothoril * -3.0, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_x(footrotl * -0.36) + next.shoulder_l.orientation = Quaternion::rotation_x(footrotl * -0.36) * Quaternion::rotation_y(0.1) * Quaternion::rotation_z(footrotl * 0.3); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothorir * -3.0, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_x(footrotr * -0.36) + next.shoulder_r.orientation = Quaternion::rotation_x(footrotr * -0.36) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(footrotr * -0.3); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -1.0 + -skeleton_attr.hand.0, skeleton_attr.hand.1 + foothoril * -4.0, skeleton_attr.hand.2 + foothoril * 1.0, ); - next.hand_l.ori = Quaternion::rotation_x(0.15 + (handhoril * -1.2).max(-0.3)) + next.hand_l.orientation = Quaternion::rotation_x(0.15 + (handhoril * -1.2).max(-0.3)) * Quaternion::rotation_y(handhoril * -0.1); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( 1.0 + skeleton_attr.hand.0, skeleton_attr.hand.1 + foothorir * -4.0, skeleton_attr.hand.2 + foothorir * 1.0, ); - next.hand_r.ori = Quaternion::rotation_x(0.15 + (handhorir * -1.2).max(-0.3)) + next.hand_r.orientation = Quaternion::rotation_x(0.15 + (handhorir * -1.2).max(-0.3)) * Quaternion::rotation_y(handhorir * 0.1); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 0.98; - next.leg_l.ori = + next.leg_l.orientation = Quaternion::rotation_z(short * 0.18) * Quaternion::rotation_x(foothoril * 0.3); next.leg_l.scale = Vec3::one() * 0.98; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 0.98; - next.leg_r.ori = + next.leg_r.orientation = Quaternion::rotation_z(short * 0.18) * Quaternion::rotation_x(foothorir * 0.3); next.leg_r.scale = Vec3::one() * 0.98; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1 + foothoril * 8.5, skeleton_attr.foot.2 + ((footvertl * 6.5).max(0.0)), ) / 8.0; - next.foot_l.ori = + next.foot_l.orientation = Quaternion::rotation_x(-0.5 + footrotl * 0.85) * Quaternion::rotation_y(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1 + foothorir * 8.5, skeleton_attr.foot.2 + ((footvertr * 6.5).max(0.0)), ) / 8.0; - next.foot_r.ori = + next.foot_r.orientation = Quaternion::rotation_x(-0.5 + footrotr * 0.85) * Quaternion::rotation_y(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); next.torso.scale = Vec3::one() / 8.0; next } diff --git a/voxygen/src/anim/src/biped_large/wield.rs b/voxygen/src/anim/src/biped_large/wield.rs index 7ab5867229..b26a8322cb 100644 --- a/voxygen/src/anim/src/biped_large/wield.rs +++ b/voxygen/src/anim/src/biped_large/wield.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BipedLargeSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct WieldAnimation; @@ -69,172 +71,175 @@ impl Animation for WieldAnimation { let shortalt = (anim_time as f32 * lab as f32 * 16.0 + PI / 2.0).sin(); - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.0); next.main.scale = Vec3::one() * 1.02; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.second.scale = Vec3::one() * 0.0; - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_l.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_l.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0 - 7.0, skeleton_attr.hand.1 - 7.0, skeleton_attr.hand.2 + 10.0, ); - next.hand_r.ori = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); + next.hand_r.orientation = Quaternion::rotation_x(0.57) * Quaternion::rotation_z(1.57); next.hand_r.scale = Vec3::one() * 1.02; if velocity < 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + breathe * 0.2, ) * 1.02; - next.head.ori = + next.head.orientation = Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + breathe * 0.5, ); - next.upper_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1 + breathe * 0.15, ); - next.lower_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.lower_torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.lower_torso.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); - next.jaw.ori = Quaternion::rotation_z(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1 * 0.0); + next.jaw.orientation = Quaternion::rotation_z(0.0); next.jaw.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0); next.tail.scale = Vec3::one(); - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + breathe * 0.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2 + breathe * 0.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(test * 0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(test * 0.0); next.torso.scale = Vec3::one() / 8.0; - next.control.offset = Vec3::new(7.0, 9.0, -10.0); - next.control.ori = Quaternion::rotation_x(test * 0.02) + next.control.position = Vec3::new(7.0, 9.0, -10.0); + next.control.orientation = Quaternion::rotation_x(test * 0.02) * Quaternion::rotation_y(test * 0.02) * Quaternion::rotation_z(test * 0.02); next.control.scale = Vec3::one(); } else { - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = + Quaternion::rotation_z(short * -0.18) * Quaternion::rotation_x(-0.05); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + shortalt * -1.5, ); - next.upper_torso.ori = Quaternion::rotation_z(short * 0.18); + next.upper_torso.orientation = Quaternion::rotation_z(short * 0.18); next.upper_torso.scale = Vec3::one(); - next.lower_torso.offset = Vec3::new( + next.lower_torso.position = Vec3::new( 0.0, skeleton_attr.lower_torso.0, skeleton_attr.lower_torso.1, ); - next.lower_torso.ori = + next.lower_torso.orientation = Quaternion::rotation_z(short * 0.15) * Quaternion::rotation_x(0.14); next.lower_torso.scale = Vec3::one() * 1.02; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothoril * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_x(0.5 + footrotl * -0.16) + next.shoulder_l.orientation = Quaternion::rotation_x(0.5 + footrotl * -0.16) * Quaternion::rotation_y(0.1) * Quaternion::rotation_z(footrotl * 0.1); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + foothorir * -1.0, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_x(0.5 + footrotr * -0.16) + next.shoulder_r.orientation = Quaternion::rotation_x(0.5 + footrotr * -0.16) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(footrotr * -0.1); next.shoulder_r.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) / 8.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) / 8.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.25); next.torso.scale = Vec3::one() / 8.0; - next.control.offset = Vec3::new(7.0, 9.0, -10.0); - next.control.ori = Quaternion::rotation_x(test * 0.02) + next.control.position = Vec3::new(7.0, 9.0, -10.0); + next.control.orientation = Quaternion::rotation_x(test * 0.02) * Quaternion::rotation_y(test * 0.02) * Quaternion::rotation_z(test * 0.02); next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/bird_medium/feed.rs b/voxygen/src/anim/src/bird_medium/feed.rs index a5ca080177..51db636ca4 100644 --- a/voxygen/src/anim/src/bird_medium/feed.rs +++ b/voxygen/src/anim/src/bird_medium/feed.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::ops::Mul; -use vek::*; pub struct FeedAnimation; @@ -40,54 +42,55 @@ impl Animation for FeedAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0 + 1.0, -2.0 + skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(duck_head_look.x) + next.head.position = + Vec3::new(0.0, skeleton_attr.head.0 + 1.0, -2.0 + skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(duck_head_look.x) * Quaternion::rotation_x(-0.3 / skeleton_attr.feed + wave_slow_cos * 0.03 + wave * 0.1); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0 + skeleton_attr.feed, -1.0 - 5.0 * (skeleton_attr.feed - 1.0) + wave_slow * 0.3 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_x(-0.5 * skeleton_attr.feed) + next.torso.orientation = Quaternion::rotation_x(-0.5 * skeleton_attr.feed) * Quaternion::rotation_y(wave_slow * 0.03); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(wave_slow_cos * 0.03); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(wave_slow_cos * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_y(0.4 - wave_slow * 0.1); + next.wing_l.orientation = Quaternion::rotation_y(0.4 - wave_slow * 0.1); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y(-0.4 + wave_slow * 0.1); + next.wing_r.orientation = Quaternion::rotation_y(-0.4 + wave_slow * 0.1); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_y(0.0); + next.leg_l.orientation = Quaternion::rotation_y(0.0); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_medium/fly.rs b/voxygen/src/anim/src/bird_medium/fly.rs index 23477c3671..8fdc61b7ed 100644 --- a/voxygen/src/anim/src/bird_medium/fly.rs +++ b/voxygen/src/anim/src/bird_medium/fly.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct FlyAnimation; @@ -28,60 +30,61 @@ impl Animation for FlyAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0 + 0.5, skeleton_attr.head.1 + center * 0.5 - 1.0, ); - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); + next.head.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0 + centeroffset * 0.6, center * 0.6 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_y(center * 0.05); + next.torso.orientation = Quaternion::rotation_y(center * 0.05); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + centeroffset * 0.6, ); - next.tail.ori = Quaternion::rotation_x(center * 0.03); + next.tail.orientation = Quaternion::rotation_x(center * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_y((0.57 + footl * 1.2).max(0.0)); + next.wing_l.orientation = Quaternion::rotation_y((0.57 + footl * 1.2).max(0.0)); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y((-0.57 + footr * 1.2).min(0.0)); + next.wing_r.orientation = Quaternion::rotation_y((-0.57 + footr * 1.2).min(0.0)); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_x(-1.3 + footl * 0.06); + next.leg_l.orientation = Quaternion::rotation_x(-1.3 + footl * 0.06); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(-1.3 + footr * 0.06); + next.leg_r.orientation = Quaternion::rotation_x(-1.3 + footr * 0.06); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_medium/idle.rs b/voxygen/src/anim/src/bird_medium/idle.rs index 33b430636b..2b7765121b 100644 --- a/voxygen/src/anim/src/bird_medium/idle.rs +++ b/voxygen/src/anim/src/bird_medium/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::ops::Mul; -use vek::*; pub struct IdleAnimation; @@ -38,53 +40,53 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(duck_head_look.x) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(duck_head_look.x) * Quaternion::rotation_x(-duck_head_look.y.abs() + wave_slow_cos * 0.03); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0, wave_slow * 0.3 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_y(wave_slow * 0.03); + next.torso.orientation = Quaternion::rotation_y(wave_slow * 0.03); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(wave_slow_cos * 0.03); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(wave_slow_cos * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_z(0.0); + next.wing_l.orientation = Quaternion::rotation_z(0.0); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y(0.0); + next.wing_r.orientation = Quaternion::rotation_y(0.0); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_y(0.0); + next.leg_l.orientation = Quaternion::rotation_y(0.0); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_medium/mod.rs b/voxygen/src/anim/src/bird_medium/mod.rs index fddd7dfa07..09096ab094 100644 --- a/voxygen/src/anim/src/bird_medium/mod.rs +++ b/voxygen/src/anim/src/bird_medium/mod.rs @@ -6,69 +6,46 @@ pub mod run; // Reexports pub use self::{feed::FeedAnimation, fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct BirdMediumSkeleton { - head: Bone, - torso: Bone, - tail: Bone, - wing_l: Bone, - wing_r: Bone, - leg_l: Bone, - leg_r: Bone, -} - -impl BirdMediumSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct BirdMediumSkeleton { + + head, + + torso, + + tail, + + wing_l, + + wing_r, + + leg_l, + + leg_r, +}); impl Skeleton for BirdMediumSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 7; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"bird_medium_compute_mats\0"; - fn bone_count(&self) -> usize { 7 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_medium_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); - ( - [ - 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()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.torso.interpolate(&target.torso, dt); - self.tail.interpolate(&target.tail, dt); - self.wing_l.interpolate(&target.wing_l, dt); - self.wing_r.interpolate(&target.wing_r, dt); - self.leg_l.interpolate(&target.leg_l, dt); - self.leg_r.interpolate(&target.leg_r, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat * Mat4::::from(self.head)), + make_bone(torso_mat), + make_bone(torso_mat * Mat4::::from(self.tail)), + make_bone(torso_mat * Mat4::::from(self.wing_l)), + make_bone(torso_mat * Mat4::::from(self.wing_r)), + make_bone(base_mat * Mat4::::from(self.leg_l)), + make_bone(base_mat * Mat4::::from(self.leg_r)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/bird_medium/run.rs b/voxygen/src/anim/src/bird_medium/run.rs index c86f16a86b..8cfb0699d4 100644 --- a/voxygen/src/anim/src/bird_medium/run.rs +++ b/voxygen/src/anim/src/bird_medium/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, BirdMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + BirdMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -28,60 +30,61 @@ impl Animation for RunAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + center * 0.5, ); - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); + next.head.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); next.head.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( 0.0, skeleton_attr.chest.0 + centeroffset * 0.6, center * 0.6 + skeleton_attr.chest.1, ) / 11.0; - next.torso.ori = Quaternion::rotation_y(center * 0.05); + next.torso.orientation = Quaternion::rotation_y(center * 0.05); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new( + next.tail.position = Vec3::new( 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + centeroffset * 0.6, ); - next.tail.ori = Quaternion::rotation_x(center * 0.03); + next.tail.orientation = Quaternion::rotation_x(center * 0.03); next.tail.scale = Vec3::one(); - next.wing_l.offset = Vec3::new( + next.wing_l.position = Vec3::new( -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_l.ori = Quaternion::rotation_y((footl * 0.35).max(0.0)); + next.wing_l.orientation = Quaternion::rotation_y((footl * 0.35).max(0.0)); next.wing_l.scale = Vec3::one() * 1.05; - next.wing_r.offset = Vec3::new( + next.wing_r.position = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, ); - next.wing_r.ori = Quaternion::rotation_y((footr * 0.35).min(0.0)); + next.wing_r.orientation = Quaternion::rotation_y((footr * 0.35).min(0.0)); next.wing_r.scale = Vec3::one() * 1.05; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + footl * 1.0, skeleton_attr.foot.2, ) / 11.0; - next.leg_l.ori = Quaternion::rotation_x(footl * 0.5); + next.leg_l.orientation = Quaternion::rotation_x(footl * 0.5); next.leg_l.scale = Vec3::one() / 11.0; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + footr * 1.0, skeleton_attr.foot.2, ) / 11.0; - next.leg_r.ori = Quaternion::rotation_x(footr * 0.5); + next.leg_r.orientation = Quaternion::rotation_x(footr * 0.5); next.leg_r.scale = Vec3::one() / 11.0; next } diff --git a/voxygen/src/anim/src/bird_small/idle.rs b/voxygen/src/anim/src/bird_small/idle.rs index f25b9a8810..94b540d9e9 100644 --- a/voxygen/src/anim/src/bird_small/idle.rs +++ b/voxygen/src/anim/src/bird_small/idle.rs @@ -1,6 +1,6 @@ use super::{super::Animation, BirdSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct IdleAnimation; @@ -21,20 +21,20 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.wing_l.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_l.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_l.scale = Vec3::one() / 10.88; - next.wing_r.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_r.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_r.scale = Vec3::one() / 10.88; next diff --git a/voxygen/src/anim/src/bird_small/jump.rs b/voxygen/src/anim/src/bird_small/jump.rs index 059480cc4a..671d838d67 100644 --- a/voxygen/src/anim/src/bird_small/jump.rs +++ b/voxygen/src/anim/src/bird_small/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, BirdSmallSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -21,20 +21,20 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.wing_l.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_l.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_l.scale = Vec3::one() / 10.88; - next.wing_r.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_r.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_r.scale = Vec3::one() / 10.88; next diff --git a/voxygen/src/anim/src/bird_small/mod.rs b/voxygen/src/anim/src/bird_small/mod.rs index dde8a69b6f..3684e6d977 100644 --- a/voxygen/src/anim/src/bird_small/mod.rs +++ b/voxygen/src/anim/src/bird_small/mod.rs @@ -5,71 +5,40 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone)] -pub struct BirdSmallSkeleton { - head: Bone, - torso: Bone, - wing_l: Bone, - wing_r: Bone, -} - -impl BirdSmallSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self { - head: Bone::default(), - torso: Bone::default(), - wing_l: Bone::default(), - wing_r: Bone::default(), - } - } -} +skeleton_impls!(struct BirdSmallSkeleton { + + head, + + torso, + + wing_l, + + wing_r, +}); impl Skeleton for BirdSmallSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 4; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"bird_small_compute_mats\0"; - fn bone_count(&self) -> usize { 4 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_small_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); - ( - [ - 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), - 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(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.torso.interpolate(&target.torso, dt); - self.wing_l.interpolate(&target.wing_l, dt); - self.wing_r.interpolate(&target.wing_r, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat * Mat4::::from(self.head)), + make_bone(torso_mat), + make_bone(torso_mat * Mat4::::from(self.wing_l)), + make_bone(torso_mat * Mat4::::from(self.wing_r)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/bird_small/run.rs b/voxygen/src/anim/src/bird_small/run.rs index 167889c0d0..80c06aa5e8 100644 --- a/voxygen/src/anim/src/bird_small/run.rs +++ b/voxygen/src/anim/src/bird_small/run.rs @@ -1,6 +1,6 @@ use super::{super::Animation, BirdSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct RunAnimation; @@ -21,20 +21,20 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.wing_l.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_l.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_l.scale = Vec3::one() / 10.88; - next.wing_r.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.wing_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.wing_r.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.wing_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.wing_r.scale = Vec3::one() / 10.88; next diff --git a/voxygen/src/anim/src/character/alpha.rs b/voxygen/src/anim/src/character/alpha.rs index dcf8507717..697c369581 100644 --- a/voxygen/src/anim/src/character/alpha.rs +++ b/voxygen/src/anim/src/character/alpha.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -58,436 +60,446 @@ impl Animation for AlphaAnimation { match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.head.offset = + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.25) + next.head.orientation = Quaternion::rotation_z(slow * -0.25) * Quaternion::rotation_x(0.0 + slow * 0.15) * Quaternion::rotation_y(slow * -0.15); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(slow * 0.4) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(slow * 0.4) * Quaternion::rotation_x(0.0 + slow * -0.2) * Quaternion::rotation_y(slow * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = next.chest.ori * -0.3; + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = next.chest.orientation * -0.3; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = next.chest.ori * -0.45; + next.shorts.position = + Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = next.chest.orientation * -0.45; - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - next.control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + next.control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); + next.control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4) * Quaternion::rotation_y(slow * -1.3) * Quaternion::rotation_z(1.4 + slow * -0.5); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, slow * -3.0 + quick * 3.0 - 4.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slow * 0.6) + next.l_foot.orientation = Quaternion::rotation_x(slow * 0.6) * Quaternion::rotation_y((slow * -0.2).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, slow * 3.0 + quick * -3.0 + 5.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slow * -0.6) + next.r_foot.orientation = Quaternion::rotation_x(slow * -0.6) * Quaternion::rotation_y((slow * 0.2).min(0.0)); next.r_foot.scale = Vec3::one(); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Dagger(_)) => { - next.head.offset = + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.25) + next.head.orientation = Quaternion::rotation_z(slow * -0.25) * Quaternion::rotation_x(0.0 + slow * 0.15) * Quaternion::rotation_y(slow * -0.15); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(slow * 0.4) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(slow * 0.4) * Quaternion::rotation_x(0.0 + slow * -0.2) * Quaternion::rotation_y(slow * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = next.chest.ori * -0.3; + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = next.chest.orientation * -0.3; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = next.chest.ori * -0.45; + next.shorts.position = + Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = next.chest.orientation * -0.45; // TODO: Fix animation - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0); + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0); next.l_hand.scale = Vec3::one() * 1.12; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); - next.l_control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - next.l_control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + next.l_control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); + next.l_control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4) * Quaternion::rotation_y(slow * -1.3) * Quaternion::rotation_z(1.4 + slow * -0.5); next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0); + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0); next.r_hand.scale = Vec3::one() * 1.12; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0); - next.r_control.offset = Vec3::new(8.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(8.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); - // next.r_control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - // next.r_control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + // next.r_control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, + // 2.0); next.r_control.orientation = + // Quaternion::rotation_x(-1.4 + slow * 0.4) // * Quaternion::rotation_y(slow * -1.3) // * Quaternion::rotation_z(1.4 + slow * -0.5); // next.r_control.scale = Vec3::one(); - // next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - // next.r_hand.ori = Quaternion::rotation_x(1.27); + // next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + // next.r_hand.orientation = Quaternion::rotation_x(1.27); // next.r_hand.scale = Vec3::one() * 1.05; - // next.control.offset = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); - // next.control.ori = Quaternion::rotation_x(-1.4 + slow * 0.4) + // next.control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0); + // next.control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4) // * Quaternion::rotation_y(slow * -1.3) // * Quaternion::rotation_z(1.4 + slow * -0.5); // next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, slow * -3.0 + quick * 3.0 - 4.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slow * 0.6) + next.l_foot.orientation = Quaternion::rotation_x(slow * 0.6) * Quaternion::rotation_y((slow * -0.2).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, slow * 3.0 + quick * -3.0 + 5.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slow * -0.6) + next.r_foot.orientation = Quaternion::rotation_x(slow * -0.6) * Quaternion::rotation_y((slow * 0.2).min(0.0)); next.r_foot.scale = Vec3::one(); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Axe(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0 + slowax * 2.0, 0.0 + skeleton_attr.head.0 + slowax * -2.0, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(slowax * 0.25) + next.head.orientation = Quaternion::rotation_z(slowax * 0.25) * Quaternion::rotation_x(0.0 + slowax * 0.2) * Quaternion::rotation_y(slowax * 0.2); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slowax * 0.2) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(slowax * 0.2) * Quaternion::rotation_x(0.0 + slowax * 0.2) * Quaternion::rotation_y(slowax * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = next.chest.ori * -0.2; + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = next.chest.orientation * -0.2; - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = next.chest.ori * -0.15; + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = next.chest.orientation * -0.15; - next.l_hand.offset = Vec3::new(-4.0, 3.0, 2.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3) + next.l_hand.position = Vec3::new(-4.0, 3.0, 2.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(-2.5, 9.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3) + next.r_hand.position = Vec3::new(-2.5, 9.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-6.0, 10.0, -5.0); - next.main.ori = Quaternion::rotation_x(1.27) + next.main.position = Vec3::new(-6.0, 10.0, -5.0); + next.main.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(-0.3) * Quaternion::rotation_z(-0.8); - next.lantern.ori = Quaternion::rotation_x(slowax * -0.7 + 0.4) + next.lantern.orientation = Quaternion::rotation_x(slowax * -0.7 + 0.4) * Quaternion::rotation_y(slowax * 0.4); - next.control.offset = Vec3::new(0.0, 0.0 + slowax * 8.2, 6.0); - next.control.ori = Quaternion::rotation_x(0.8) + next.control.position = Vec3::new(0.0, 0.0 + slowax * 8.2, 6.0); + next.control.orientation = Quaternion::rotation_x(0.8) * Quaternion::rotation_y(-0.3) * Quaternion::rotation_z(-0.7 + slowax * -1.9); next.control.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = + Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(3.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(3.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.head.offset = + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slower * 0.03) + next.head.orientation = Quaternion::rotation_z(slower * 0.03) * Quaternion::rotation_x(slowersmooth * 0.1) * Quaternion::rotation_y(slower * 0.05 + slowersmooth * 0.06) * Quaternion::rotation_z((slowersmooth * -0.4).max(0.0)); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slower * 0.18 + slowersmooth * 0.15) - * Quaternion::rotation_x(0.0 + slower * 0.18 + slowersmooth * 0.15) - * Quaternion::rotation_y(slower * 0.18 + slowersmooth * 0.15); + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = + Quaternion::rotation_z(slower * 0.18 + slowersmooth * 0.15) + * Quaternion::rotation_x(0.0 + slower * 0.18 + slowersmooth * 0.15) + * Quaternion::rotation_y(slower * 0.18 + slowersmooth * 0.15); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) - * Quaternion::rotation_x(0.0 + slower * -0.1) - * Quaternion::rotation_y(slower * -0.1); + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = + Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) + * Quaternion::rotation_x(0.0 + slower * -0.1) + * Quaternion::rotation_y(slower * -0.1); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) - * Quaternion::rotation_x(0.0 + slower * -0.1) - * Quaternion::rotation_y(slower * -0.1); + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = + Quaternion::rotation_z(slower * -0.1 + slowersmooth * -0.075) + * Quaternion::rotation_x(0.0 + slower * -0.1) + * Quaternion::rotation_y(slower * -0.1); - next.lantern.ori = Quaternion::rotation_x(slower * -0.7 + 0.4) + next.lantern.orientation = Quaternion::rotation_x(slower * -0.7 + 0.4) * Quaternion::rotation_y(slower * 0.4); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; if velocity > 0.5 { - next.l_foot.offset = + next.l_foot.position = Vec3::new(-skeleton_attr.foot.0, foot * -6.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4) + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4) * Quaternion::rotation_z((slower * 0.3).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, foot * 6.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4) + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4) * Quaternion::rotation_z((slower * 0.3).max(0.0)); next.r_foot.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.15); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.15); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.5, skeleton_attr.foot.2 + (slower * 2.5).max(0.0), ); - next.l_foot.ori = Quaternion::rotation_x(slower * -0.2 - 0.2) + next.l_foot.orientation = Quaternion::rotation_x(slower * -0.2 - 0.2) * Quaternion::rotation_z((slower * 1.0).max(0.0)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 3.5 - slower * 2.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slower * 0.1) + next.r_foot.orientation = Quaternion::rotation_x(slower * 0.1) * Quaternion::rotation_z((slower * 0.5).max(0.0)); next.r_foot.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - //next.control.offset = Vec3::new(-4.0, 3.0 + slower * 2.0, 5.0 + slower * - // 5.0); next.control.ori = Quaternion::rotation_x() + //next.control.position = Vec3::new(-4.0, 3.0 + slower * 2.0, 5.0 + slower * + // 5.0); next.control.orientation = Quaternion::rotation_x() // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(1.4); next.control.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(-1.5 + slower * 1.5) + next.control.position = Vec3::new(-8.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(-1.5 + slower * 1.5) * Quaternion::rotation_y(slowersmooth * 0.35 - 0.3) * Quaternion::rotation_z(1.4 + slowersmooth * 0.2); next.control.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Staff(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, 0.0 + skeleton_attr.head.0, /* + decel * 0.8 */ // Had some clipping issues skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(decel * 0.25) + next.head.orientation = Quaternion::rotation_z(decel * 0.25) * Quaternion::rotation_x(0.0 + decel * 0.1) * Quaternion::rotation_y(decel * -0.1); - next.chest.ori = Quaternion::rotation_z(decel * -0.2) + next.chest.orientation = Quaternion::rotation_z(decel * -0.2) * Quaternion::rotation_x(0.0 + decel * -0.2) * Quaternion::rotation_y(decel * 0.2); - next.belt.ori = Quaternion::rotation_z(decel * -0.1) + next.belt.orientation = Quaternion::rotation_z(decel * -0.1) * Quaternion::rotation_x(0.0 + decel * -0.1) * Quaternion::rotation_y(decel * 0.1); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(decel * -0.08) + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = Quaternion::rotation_z(decel * -0.08) * Quaternion::rotation_x(0.0 + decel * -0.08) * Quaternion::rotation_y(decel * 0.08); - next.l_hand.offset = Vec3::new(0.0, 1.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(0.0, 1.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(0.0, 0.0, 10.0); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.0, 0.0, 10.0); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -4.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -4.0); + next.main.orientation = Quaternion::rotation_x(-0.3); - next.control.offset = Vec3::new(-8.0 - slow * 1.0, 3.0 - slow * 5.0, 0.0); - next.control.ori = Quaternion::rotation_x(-1.2) + next.control.position = Vec3::new(-8.0 - slow * 1.0, 3.0 - slow * 5.0, 0.0); + next.control.orientation = Quaternion::rotation_x(-1.2) * Quaternion::rotation_y(slow * 1.5) * Quaternion::rotation_z(1.4 + slow * 0.5); next.control.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, Some(ToolKind::Shield(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, 0.0 + skeleton_attr.head.0 + decel * 0.8, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(decel * 0.25) + next.head.orientation = Quaternion::rotation_z(decel * 0.25) * Quaternion::rotation_x(0.0 + decel * 0.1) * Quaternion::rotation_y(decel * -0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(decel * -0.2) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(decel * -0.2) * Quaternion::rotation_x(0.0 + decel * -0.2) * Quaternion::rotation_y(decel * 0.2); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.belt.offset = Vec3::new(0.0, 0.0, 0.0); - next.belt.ori = Quaternion::rotation_z(decel * -0.1) + next.belt.position = Vec3::new(0.0, 0.0, 0.0); + next.belt.orientation = Quaternion::rotation_z(decel * -0.1) * Quaternion::rotation_x(0.0 + decel * -0.1) * Quaternion::rotation_y(decel * 0.1); - next.shorts.offset = Vec3::new(0.0, 0.0, 0.0); - next.belt.ori = Quaternion::rotation_z(decel * -0.08) + next.shorts.position = Vec3::new(0.0, 0.0, 0.0); + next.belt.orientation = Quaternion::rotation_z(decel * -0.08) * Quaternion::rotation_x(0.0 + decel * -0.08) * Quaternion::rotation_y(decel * 0.08); - next.l_control.offset = + next.l_control.position = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.l_control.ori = Quaternion::rotation_z(-0.8) + next.l_control.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.0 + accel_med * -0.8) * Quaternion::rotation_y(0.0 + accel_med * -0.4); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0); + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_z(0.0); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_z(0.0); - next.r_control.offset = Vec3::new(8.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(8.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0); + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0); next.r_hand.scale = Vec3::one() * 1.01; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0); }, Some(ToolKind::Debug(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + decel * 0.8, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_x(0.0); + next.head.orientation = Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(decel * -0.2) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(decel * -0.2) * Quaternion::rotation_x(0.0 + decel * -0.2) * Quaternion::rotation_y(decel * 0.2); - next.l_hand.offset = + next.l_hand.position = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.l_hand.ori = Quaternion::rotation_z(-0.8) + next.l_hand.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(accel_med * -0.8) * Quaternion::rotation_y(accel_med * -0.4); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = + next.r_hand.position = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_z(-0.8) + next.r_hand.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(accel_med * -0.8) * Quaternion::rotation_y(accel_med * -0.4); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.main.ori = Quaternion::rotation_z(-0.8) + next.main.position = + Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); + next.main.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.0 + accel_med * -0.8) * Quaternion::rotation_y(0.0 + accel_med * -0.4); next.main.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }, _ => {}, } - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, @@ -495,7 +507,7 @@ impl Animation for AlphaAnimation { next.lantern.scale = Vec3::one() * 0.65; next.l_shoulder.scale = Vec3::one() * 1.1; next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/beta.rs b/voxygen/src/anim/src/character/beta.rs index 720ce56bf3..739d4ca406 100644 --- a/voxygen/src/anim/src/character/beta.rs +++ b/voxygen/src/anim/src/character/beta.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; -use vek::*; pub struct BetaAnimation; @@ -46,84 +48,85 @@ impl Animation for BetaAnimation { ) = active_tool_kind { //INTENTION: SWORD - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.18) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(slow * -0.18) * Quaternion::rotation_x(-0.1 + slow * -0.28) * Quaternion::rotation_y(0.2 + slow * 0.18); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slow * 0.2) + next.chest.position = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(slow * 0.2) * Quaternion::rotation_x(slow * 0.2) * Quaternion::rotation_y(slow * -0.1); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slow * 0.1) + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = Quaternion::rotation_z(slow * 0.1) * Quaternion::rotation_x(slow * 0.1) * Quaternion::rotation_y(slow * -0.04); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(slow * 0.1) + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.shorts.orientation = Quaternion::rotation_z(slow * 0.1) * Quaternion::rotation_x(slow * 0.1) * Quaternion::rotation_y(slow * -0.05); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3); - next.control.offset = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0); - next.control.ori = Quaternion::rotation_x(-1.4) + next.control.position = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0); + next.control.orientation = Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(slow * 2.0 + 0.7) * Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, footquick * -9.5, skeleton_attr.foot.2, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(footquick * 0.3) * Quaternion::rotation_y(footquick * -0.6); - next.r_foot.offset = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(footquick * -0.3) * Quaternion::rotation_y(footquick * 0.2); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/block.rs b/voxygen/src/anim/src/character/block.rs index b215071cd8..67099c8d58 100644 --- a/voxygen/src/anim/src/character/block.rs +++ b/voxygen/src/anim/src/character/block.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct Input { pub attack: bool, @@ -42,133 +44,135 @@ impl Animation for BlockAnimation { .sin() * 0.15, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, -1.0 + skeleton_attr.head.0, skeleton_attr.head.1 + 19.5 + wave_ultra_slow * 0.2, ); - next.head.ori = Quaternion::rotation_x(-0.25); + next.head.orientation = Quaternion::rotation_x(-0.25); next.head.scale = Vec3::one() * 1.01 * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 5.0 + wave_ultra_slow * 0.2); - next.chest.ori = + next.chest.position = + Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 5.0 + wave_ultra_slow * 0.2); + next.chest.orientation = Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(wave_ultra_slow_cos * 0.01); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 3.0 + wave_ultra_slow * 0.2); - next.belt.ori = + next.belt.position = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 3.0 + wave_ultra_slow * 0.2); + next.belt.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(wave_ultra_slow_cos * 0.008); next.belt.scale = Vec3::one() * 1.01; - next.shorts.offset = Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 1.0 + wave_ultra_slow * 0.2); - next.shorts.ori = Quaternion::rotation_x(0.1); + next.shorts.position = + Vec3::new(0.0 + wave_slow_cos * 0.2, 0.0, 1.0 + wave_ultra_slow * 0.2); + next.shorts.orientation = Quaternion::rotation_x(0.1); next.shorts.scale = Vec3::one(); match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -6.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 0.0, -6.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0, 13.0, 8.0); - next.control.ori = Quaternion::rotation_x(0.2) + next.control.position = Vec3::new(-8.0, 13.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-1.57); next.control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 3.5, 6.5); - next.l_hand.ori = Quaternion::rotation_x(2.07) + next.l_hand.position = Vec3::new(-7.0, 3.5, 6.5); + next.l_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, 3.75); - next.r_hand.ori = Quaternion::rotation_x(2.07) + next.r_hand.position = Vec3::new(7.0, 2.5, 3.75); + next.r_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, 5.5); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, 5.5); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(-0.85); next.main.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( + next.main.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 4.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); }, Some(ToolKind::Shield(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( + next.main.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 4.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); @@ -176,34 +180,35 @@ impl Animation for BlockAnimation { _ => {}, } - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 5.0, 0.0); - next.glider.ori = Quaternion::rotation_y(0.0); + next.glider.position = Vec3::new(0.0, 5.0, 0.0); + next.glider.orientation = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/blockidle.rs b/voxygen/src/anim/src/character/blockidle.rs index a08717f268..43a5ff1fb1 100644 --- a/voxygen/src/anim/src/character/blockidle.rs +++ b/voxygen/src/anim/src/character/blockidle.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct Input { pub attack: bool, @@ -41,94 +43,94 @@ impl Animation for BlockIdleAnimation { .sin() * 0.15, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, -1.0 + skeleton_attr.head.0, skeleton_attr.head.1 + wave_ultra_slow * 0.2, ); - next.head.ori = Quaternion::rotation_x(0.0); + next.head.orientation = Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.01 * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, 0.0, skeleton_attr.chest.1 + wave_ultra_slow * 0.2, ); - next.chest.ori = Quaternion::rotation_y(wave_ultra_slow_cos * 0.01); + next.chest.orientation = Quaternion::rotation_y(wave_ultra_slow_cos * 0.01); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new( + next.belt.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, 0.0, skeleton_attr.belt.1 + wave_ultra_slow * 0.2, ); - next.belt.ori = + next.belt.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(wave_ultra_slow_cos * 0.008); next.belt.scale = Vec3::one() * 1.01; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0 + wave_slow_cos * 0.2, 0.0, skeleton_attr.shorts.1 + wave_ultra_slow * 0.2, ); - next.shorts.ori = Quaternion::rotation_x(0.1); + next.shorts.orientation = Quaternion::rotation_x(0.1); next.shorts.scale = Vec3::one(); match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -6.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 0.0, -6.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0, 13.0, 8.0); - next.control.ori = Quaternion::rotation_x(0.2) + next.control.position = Vec3::new(-8.0, 13.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-1.57); next.control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, 0.0 + wave_ultra_slow * 1.0, ); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.0 + wave_ultra_slow_cos * 0.5, -2.0 + wave_ultra_slow * 1.0, ); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(-6.0, 4.5, 0.0 + wave_ultra_slow * 1.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); - next.l_hand.ori = Quaternion::rotation_x(2.07) + next.l_hand.position = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); + next.l_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); - next.r_hand.ori = Quaternion::rotation_x(2.07) + next.r_hand.position = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); + next.r_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(-0.85); next.main.scale = Vec3::one(); @@ -138,44 +140,44 @@ impl Animation for BlockIdleAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.main.scale = Vec3::one(); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); @@ -185,60 +187,60 @@ impl Animation for BlockIdleAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); - next.l_hand.ori = Quaternion::rotation_x(2.07) + next.l_hand.position = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5); + next.l_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); - next.r_hand.ori = Quaternion::rotation_x(2.07) + next.r_hand.position = Vec3::new(7.0, 2.5 + wave_ultra_slow * 2.0, 3.75); + next.r_hand.orientation = Quaternion::rotation_x(2.07) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.2); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75 + wave_ultra_slow * 2.0, 5.5); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(-0.85); next.main.scale = Vec3::one(); @@ -252,44 +254,44 @@ impl Animation for BlockIdleAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.main.scale = Vec3::one(); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(3.0, 7.0, 5.0); - next.r_control.ori = Quaternion::rotation_x(0.5 * PI) + next.r_control.position = Vec3::new(3.0, 7.0, 5.0); + next.r_control.orientation = Quaternion::rotation_x(0.5 * PI) * Quaternion::rotation_y(0.5 * PI) * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); @@ -298,41 +300,43 @@ impl Animation for BlockIdleAnimation { _ => {}, } - next.l_foot.offset = Vec3::new(-3.4, 0.3, skeleton_attr.foot.1 + wave_ultra_slow_cos * 0.1); - next.l_foot.ori = Quaternion::rotation_x(-0.3); + next.l_foot.position = + Vec3::new(-3.4, 0.3, skeleton_attr.foot.1 + wave_ultra_slow_cos * 0.1); + next.l_foot.orientation = Quaternion::rotation_x(-0.3); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(3.4, 1.2, skeleton_attr.foot.1 + wave_ultra_slow * 0.1); - next.r_foot.ori = Quaternion::rotation_x(0.3); + next.r_foot.position = Vec3::new(3.4, 1.2, skeleton_attr.foot.1 + wave_ultra_slow * 0.1); + next.r_foot.orientation = Quaternion::rotation_x(0.3); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one(); + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/charge.rs b/voxygen/src/anim/src/character/charge.rs index 11dd3568bb..5589299236 100644 --- a/voxygen/src/anim/src/character/charge.rs +++ b/voxygen/src/anim/src/character/charge.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct ChargeAnimation; @@ -62,7 +64,7 @@ impl Animation for ChargeAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -74,73 +76,75 @@ impl Animation for ChargeAnimation { 0.0 } * 1.3; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( stop * -2.0, -3.5 + stop * 2.5 + skeleton_attr.head.0, skeleton_attr.head.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(stop * -1.0 + tilt * -2.0) * Quaternion::rotation_y(stop * -0.3); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(stop * 1.2 + stress * stop * 0.02 + tilt * -2.0); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = + Quaternion::rotation_z(stop * 1.2 + stress * stop * 0.02 + tilt * -2.0); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(stop * -0.5 + tilt * 2.0); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(stop * -0.5 + tilt * 2.0); - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(stop * -0.7 + tilt * 4.0); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(stop * -0.7 + tilt * 4.0); match active_tool_kind { //TODO: Inventory Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.0, -2.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.0, -2.0, -5.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(9.0, 1.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(9.2, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(9.2, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new( + next.control.position = Vec3::new( -7.0 + quick * 3.5 * (1.0 / (stopa + 0.1)), 6.0 + quicka * 3.5 * (1.0 / (stopa + 0.1)), 6.0 - stop * 3.0, ); - next.control.ori = + next.control.orientation = Quaternion::rotation_x(stop * -0.2) * Quaternion::rotation_z(stop * 0.2); next.control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(1.0, -2.0 + stop * -1.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(1.0, -2.0 + stop * -1.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(4.9, 1.0, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(4.9, 1.0, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, -1.0, -14.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, -1.0, -14.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.hold.offset = Vec3::new(0.4, -0.3, -5.8); - next.hold.ori = Quaternion::rotation_x(-1.6) + next.hold.position = Vec3::new(0.4, -0.3, -5.8); + next.hold.orientation = Quaternion::rotation_x(-1.6) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.0); next.hold.scale = Vec3::one() * 1.0; - next.control.offset = Vec3::new(-10.0 + stop * 13.0, 6.0 + stop * 4.0, 8.0); - next.control.ori = Quaternion::rotation_x(0.0) + next.control.position = Vec3::new(-10.0 + stop * 13.0, 6.0 + stop * 4.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(stop * -0.4) * Quaternion::rotation_z(stop * -0.6); next.control.scale = Vec3::one(); @@ -149,76 +153,77 @@ impl Animation for ChargeAnimation { } if velocity > 0.2 { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0 - foot * 1.5, skeleton_attr.foot.1 + foote * 2.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(foote * -0.1) + next.l_foot.orientation = Quaternion::rotation_x(foote * -0.1) * Quaternion::rotation_z(0.4) * Quaternion::rotation_y(0.15); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0 + foot * 1.5, skeleton_attr.foot.1 + foote * -1.5, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(0.0) + next.r_foot.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_z(0.4) * Quaternion::rotation_y(0.0); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.5 + stop * -1.3, skeleton_attr.foot.2 + tilt * -4.0 * foot, ); - next.l_foot.ori = Quaternion::rotation_x(stop * -0.2 - 0.2 + stop * stress * 0.02) - * Quaternion::rotation_z(stop * 0.1) - * Quaternion::rotation_y(stop * 0.08); + next.l_foot.orientation = + Quaternion::rotation_x(stop * -0.2 - 0.2 + stop * stress * 0.02) + * Quaternion::rotation_z(stop * 0.1) + * Quaternion::rotation_y(stop * 0.08); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 3.5 + stop * 1.5, skeleton_attr.foot.2 + tilt * 4.0 * foot, ); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(stop * 0.1) * Quaternion::rotation_z(stop * 0.1); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.3); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.3); next.back.scale = Vec3::one() * 1.02; - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/climb.rs b/voxygen/src/anim/src/character/climb.rs index aee0be362b..ec4c311202 100644 --- a/voxygen/src/anim/src/character/climb.rs +++ b/voxygen/src/anim/src/character/climb.rs @@ -1,9 +1,10 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; - pub struct ClimbAnimation; impl Animation for ClimbAnimation { @@ -61,251 +62,259 @@ impl Animation for ClimbAnimation { * 0.15, ); let stagnant = if speed > -0.7 { 1.0 } else { 0.0 }; //sets static position when there is no movement + + next.hold.scale = Vec3::one() * 0.0; + if speed > 0.7 || lateral > 0.1 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -4.0 + skeleton_attr.head.0, skeleton_attr.head.1 + smootha * 0.2, ); - next.head.ori = Quaternion::rotation_z(smooth * 0.1) + next.head.orientation = Quaternion::rotation_z(smooth * 0.1) * Quaternion::rotation_x(0.6) * Quaternion::rotation_y(quick * 0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + smootha * 1.1, ); - next.chest.ori = Quaternion::rotation_z(quick * 0.25) + next.chest.orientation = Quaternion::rotation_z(quick * 0.25) * Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(quick * -0.12); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(quick * 0.0) * Quaternion::rotation_x(0.0); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1); + next.belt.orientation = + Quaternion::rotation_z(quick * 0.0) * Quaternion::rotation_x(0.0); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.2); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.2); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0 + 1.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(quick * 0.0) + next.shorts.orientation = Quaternion::rotation_z(quick * 0.0) * Quaternion::rotation_x(0.1) * Quaternion::rotation_y(quick * 0.10); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, 4.0 + skeleton_attr.hand.1 + quicka * 1.5, 5.0 + skeleton_attr.hand.2 - quick * 4.0, ); - next.l_hand.ori = Quaternion::rotation_x(2.2 + quicka * 0.5); + next.l_hand.orientation = Quaternion::rotation_x(2.2 + quicka * 0.5); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, 5.0 + skeleton_attr.hand.1 - quicka * 1.5, 5.0 + skeleton_attr.hand.2 + quick * 4.0, ); - next.r_hand.ori = Quaternion::rotation_x(2.2 - quicka * 0.5); + next.r_hand.orientation = Quaternion::rotation_x(2.2 - quicka * 0.5); next.r_hand.scale = Vec3::one(); match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 5.0 + skeleton_attr.foot.1, skeleton_attr.foot.2 + quick * 2.5, ); - next.l_foot.ori = Quaternion::rotation_x(0.2 - quicka * 0.5); + next.l_foot.orientation = Quaternion::rotation_x(0.2 - quicka * 0.5); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, skeleton_attr.foot.2 - quick * 2.5, ); - next.r_foot.ori = Quaternion::rotation_x(0.2 + quicka * 0.5); + next.r_foot.orientation = Quaternion::rotation_x(0.2 + quicka * 0.5); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(smootha * 0.15); + next.l_shoulder.orientation = Quaternion::rotation_x(smootha * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(smooth * 0.15); + next.r_shoulder.orientation = Quaternion::rotation_x(smooth * 0.15); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-7.0, -5.0, 18.0); - next.main.ori = + next.main.position = Vec3::new(-7.0, -5.0, 18.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + smootha * 0.25); next.main.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_y(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_y(0.0); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(smooth * -0.3) * Quaternion::rotation_y(smooth * -0.3); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, -0.2 + smooth * -0.08, 0.4) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, -0.2 + smooth * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -1.0 - stagnant + skeleton_attr.head.0, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_x( + next.head.orientation = Quaternion::rotation_x( -0.25 * (1.0 - stagnant) + stagnant * 2.0 * head_look.x.abs(), ) * Quaternion::rotation_z(stagnant * 3.5 * head_look.x.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 1.0 + skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(0.6 * stagnant) + next.chest.position = + Vec3::new(0.0, 1.0 + skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(0.6 * stagnant) * Quaternion::rotation_x((0.2 + drop * 0.05) * (1.0 - stagnant)); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + 0.5, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(0.1 + dropa * 0.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + 0.5, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_x(0.1 + dropa * 0.1); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x( + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x( -0.2 + dropa * 0.1 - 0.15 * (1.0 - stagnant) + stagnant * 0.1, ); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0 + 1.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(0.1 + dropa * 0.12 * (1.0 - stagnant)); + next.shorts.orientation = Quaternion::rotation_x(0.1 + dropa * 0.12 * (1.0 - stagnant)); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, 7.5 + stagnant * -5.0 + skeleton_attr.hand.1, 7.0 + stagnant * -7.0 + skeleton_attr.hand.2 + dropa * -1.0 * (1.0 - stagnant), ); - next.l_hand.ori = Quaternion::rotation_x(2.2 + stagnant * -1.4) + next.l_hand.orientation = Quaternion::rotation_x(2.2 + stagnant * -1.4) * Quaternion::rotation_y((0.3 + dropa * 0.1) * (1.0 - stagnant)); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, 7.5 + stagnant * -2.5 + skeleton_attr.hand.1, 5.0 + skeleton_attr.hand.2 + drop * -1.0 * (1.0 - stagnant), ); - next.r_hand.ori = Quaternion::rotation_x(2.2) + next.r_hand.orientation = Quaternion::rotation_x(2.2) * Quaternion::rotation_y(-0.3 + drop * 0.1 * (1.0 - stagnant)); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 4.0 + stagnant * 3.0 + skeleton_attr.foot.1, 1.0 + skeleton_attr.foot.2 + drop * -2.0 * (1.0 - stagnant), ); - next.l_foot.ori = Quaternion::rotation_x(0.55 + drop * 0.1 * (1.0 - stagnant)); + next.l_foot.orientation = Quaternion::rotation_x(0.55 + drop * 0.1 * (1.0 - stagnant)); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + stagnant * 4.0 + skeleton_attr.foot.1, -2.0 + skeleton_attr.foot.2 + smooth * 1.0 * (1.0 - stagnant), ); - next.r_foot.ori = Quaternion::rotation_x(0.2 + smooth * 0.15 * (1.0 - stagnant)); + next.r_foot.orientation = + Quaternion::rotation_x(0.2 + smooth * 0.15 * (1.0 - stagnant)); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.main.offset = Vec3::new(-7.0, -5.0, 18.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 18.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_y(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_y(0.0); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, -0.2, 0.4) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.4) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; }; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/dance.rs b/voxygen/src/anim/src/character/dance.rs index a79e16ad9d..6abdd7328e 100644 --- a/voxygen/src/anim/src/character/dance.rs +++ b/voxygen/src/anim/src/character/dance.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct DanceAnimation; @@ -54,136 +56,142 @@ impl Animation for DanceAnimation { * 0.15, ); - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(short * -0.6) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(short * -0.6) * Quaternion::rotation_x(0.2 + head_look.y.max(0.0) + shorte.abs() * -0.2); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + shortealt * 1.5, ); - next.chest.ori = Quaternion::rotation_z(short * 0.35) + next.chest.orientation = Quaternion::rotation_z(short * 0.35) * Quaternion::rotation_y(shorte * 0.08) * Quaternion::rotation_x(foot * 0.07); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(shorte * 0.25); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(shorte * 0.25); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.25 + shorte * 0.1 + noisea * 0.1 + noiseb * 0.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = + Quaternion::rotation_x(-0.25 + shorte * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(foot * 0.35); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(foot * 0.35); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( 1.0 - skeleton_attr.hand.0, 2.0 + skeleton_attr.hand.1 + shortealt * -3.0, skeleton_attr.hand.2 + shortealt * -0.75, ); - next.l_hand.ori = Quaternion::rotation_x(1.4 + foot * 0.15) * Quaternion::rotation_y(0.2); + next.l_hand.orientation = + Quaternion::rotation_x(1.4 + foot * 0.15) * Quaternion::rotation_y(0.2); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, 2.0 + skeleton_attr.hand.1 + shortealt * 3.0, skeleton_attr.hand.2 + shortealt * 0.75, ); - next.r_hand.ori = Quaternion::rotation_x(1.4 + foot * -0.15) * Quaternion::rotation_y(-0.2); + next.r_hand.orientation = + Quaternion::rotation_x(1.4 + foot * -0.15) * Quaternion::rotation_y(-0.2); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0 + foot * 0.8, 1.5 + -skeleton_attr.foot.1 + foot * -4.0, skeleton_attr.foot.2 + 2.0, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.3) * Quaternion::rotation_z(short * -0.15); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0 + foot * 0.8, 1.5 + -skeleton_attr.foot.1 + foot * 4.0, skeleton_attr.foot.2 + 2.0, ); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.3) * Quaternion::rotation_z(short * 0.15); + next.r_foot.orientation = + Quaternion::rotation_x(foot * 0.3) * Quaternion::rotation_z(short * 0.15); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(shorte * 0.15); + next.l_shoulder.orientation = Quaternion::rotation_x(shorte * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(shorte * -0.15); + next.r_shoulder.orientation = Quaternion::rotation_x(shorte * -0.15); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(short * -0.2); + next.torso.position = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(short * -0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/dash.rs b/voxygen/src/anim/src/character/dash.rs index 4f6f73e431..8e1cf865c3 100644 --- a/voxygen/src/anim/src/character/dash.rs +++ b/voxygen/src/anim/src/character/dash.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; -use vek::*; pub struct Input { pub attack: bool, @@ -40,84 +42,86 @@ impl Animation for DashAnimation { match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); - next.chest.ori = Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); + next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); + next.chest.orientation = + Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); - next.shorts.offset = Vec3::new(0.0, 3.0, -3.0); - next.shorts.ori = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, 3.0, -3.0); + next.shorts.orientation = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); - next.control.ori = + next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); + next.control.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(1.1 + slow * 0.2); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4 - 0.8); + next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8); - next.r_foot.offset = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4 - 0.8); + next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8); }, Some(ToolKind::Dagger(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); - next.chest.ori = Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); + next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); + next.chest.orientation = + Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2); - next.shorts.offset = Vec3::new(0.0, 3.0, -3.0); - next.shorts.ori = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, 3.0, -3.0); + next.shorts.orientation = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); - next.control.ori = + next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0); + next.control.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(1.1 + slow * 0.2); next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4 - 0.8); + next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8); - next.r_foot.offset = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4 - 0.8); + next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8); }, _ => {}, } @@ -125,68 +129,69 @@ impl Animation for DashAnimation { match second_tool_kind { //TODO: Inventory Some(ToolKind::Dagger(_)) => { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); - next.chest.ori = Quaternion::rotation_x(0.0); + next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0); + next.chest.orientation = Quaternion::rotation_x(0.0); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_x(0.0); - next.shorts.offset = Vec3::new(0.0, 3.0, -3.0); - next.shorts.ori = Quaternion::rotation_x(0.0); + next.shorts.position = Vec3::new(0.0, 3.0, -3.0); + next.shorts.orientation = Quaternion::rotation_x(0.0); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(-8.0, -10.0, 0.0); + next.l_control.position = Vec3::new(-8.0, -10.0, 0.0); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0); + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0); next.l_hand.scale = Vec3::one() * 1.04; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); next.main.scale = Vec3::one(); - next.r_control.offset = Vec3::new(8.0, 10.0, 0.0); + next.r_control.position = Vec3::new(8.0, 10.0, 0.0); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0); + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0); next.r_hand.scale = Vec3::one() * 1.05; - next.second.offset = Vec3::new(0.0, 6.0, -1.0); - next.second.ori = Quaternion::rotation_x(-0.3); + next.second.position = Vec3::new(0.0, 6.0, -1.0); + next.second.orientation = Quaternion::rotation_x(-0.3); next.second.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -0.4 - 0.8); + next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8); - next.r_foot.offset = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 0.4 - 0.8); + next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8); }, _ => {}, } - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4); + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; diff --git a/voxygen/src/anim/src/character/equip.rs b/voxygen/src/anim/src/character/equip.rs index df02fc16e5..ff07260341 100644 --- a/voxygen/src/anim/src/character/equip.rs +++ b/voxygen/src/anim/src/character/equip.rs @@ -2,7 +2,7 @@ use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct EquipAnimation; @@ -41,141 +41,150 @@ impl Animation for EquipAnimation { match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(-0.2); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(-0.2); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.3); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -6.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, -6.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = + next.control.position = Vec3::new(-3.0 + equip_slowa * -1.5, -5.0, 12.0 + equip_slow * 1.5); - next.control.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.control.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { - next.l_hand.offset = Vec3::new(-4.0, 3.0, 6.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3) + next.l_hand.position = Vec3::new(-4.0, 3.0, 6.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(-2.5, 9.0, 4.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3) + next.r_hand.position = Vec3::new(-2.5, 9.0, 4.0); + next.r_hand.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(3.14 - 0.3) * Quaternion::rotation_y(-0.8); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-6.0, 10.0, -1.0); - next.main.ori = Quaternion::rotation_x(1.27) + next.main.position = Vec3::new(-6.0, 10.0, -1.0); + next.main.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(-0.3) * Quaternion::rotation_z(-0.8); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.2) * Quaternion::rotation_y(-0.3); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = + Quaternion::rotation_x(0.2) * Quaternion::rotation_y(-0.3); next.control.scale = Vec3::one(); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 5.5, 3.5); - next.l_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.32); + next.l_hand.position = Vec3::new(-7.0, 5.5, 3.5); + next.l_hand.orientation = + Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.32); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(8.0, 7.75, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.22); + next.r_hand.position = Vec3::new(8.0, 7.75, 0.0); + next.r_hand.orientation = + Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.22); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(6.0, 7.0, 0.0); - next.main.ori = Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(6.0, 7.0, 0.0); + next.main.orientation = + Quaternion::rotation_y(-1.35) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); - next.control.offset = + next.control.position = Vec3::new(-3.0 + equip_slowa * -1.5, -12.0, 12.0 + equip_slow * 1.5); - next.control.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(1.35 + 2.5); + next.control.orientation = + Quaternion::rotation_x(0.0) * Quaternion::rotation_y(1.35 + 2.5); next.control.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.0, -2.0, -5.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.0, -2.0, -5.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(9.0, 1.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(11.0, 9.0, 10.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(11.0, 9.0, 10.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(wave_ultra_slow * 0.2) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.2) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(wave_ultra_slow_cos * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Shield(_)) => { - next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3); + next.l_hand.position = Vec3::new(-6.0, 3.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3); + next.r_hand.position = Vec3::new(-6.0, 3.0, -2.0); + next.r_hand.orientation = Quaternion::rotation_x(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(-6.0, 4.5, 0.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.main.position = Vec3::new(-6.0, 4.5, 0.0); + next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(2.0, 1.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(2.0, 1.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(5.9, 4.5, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(5.9, 4.5, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(wave_ultra_slow * 0.2) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.2) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(wave_ultra_slow_cos * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Dagger(_)) => { // TODO: Fix animation - // next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); - // next.l_hand.ori = Quaternion::rotation_x(-0.3); + // next.l_hand.position = Vec3::new(-6.0, 3.5, 0.0); + // next.l_hand.orientation = Quaternion::rotation_x(-0.3); // next.l_hand.scale = Vec3::one() * 1.01; - // next.main.offset = Vec3::new(-6.0, 4.5, 0.0); - // next.main.ori = Quaternion::rotation_x(-0.3); + // next.main.position = Vec3::new(-6.0, 4.5, 0.0); + // next.main.orientation = Quaternion::rotation_x(-0.3); next.main.scale = Vec3::one(); - // next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - // next.r_hand.ori = Quaternion::rotation_x(-0.3); + // next.r_hand.position = Vec3::new(-6.0, 3.0, -2.0); + // next.r_hand.orientation = Quaternion::rotation_x(-0.3); // next.r_hand.scale = Vec3::one() * 1.01; }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0); - next.l_hand.ori = Quaternion::rotation_x(1.27 + wave * 0.25) + next.l_hand.position = Vec3::new(-7.0, 4.0, 3.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27 + wave * 0.25) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, -1.25); - next.r_hand.ori = + next.r_hand.position = Vec3::new(7.0, 2.5, -1.25); + next.r_hand.orientation = Quaternion::rotation_x(1.27 + wave * 0.25) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, -2.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, -2.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.27) * Quaternion::rotation_z(wave * -0.25); next.main.scale = Vec3::one(); @@ -194,40 +203,41 @@ impl Animation for EquipAnimation { .sin() * 0.1, ); + next.hold.scale = Vec3::one() * 0.0; if velocity > 0.5 { - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.2); } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + short * 0.2, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(wave_ultra_slow_cos * 0.035 - 0.2); + next.l_foot.orientation = Quaternion::rotation_x(wave_ultra_slow_cos * 0.035 - 0.2); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(wave_ultra_slow * 0.035); + next.r_foot.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.035); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; } next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/glidewield.rs b/voxygen/src/anim/src/character/glidewield.rs index 6dfb6dbfb6..c9e5bc36b1 100644 --- a/voxygen/src/anim/src/character/glidewield.rs +++ b/voxygen/src/anim/src/character/glidewield.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct GlideWieldAnimation; @@ -96,7 +98,7 @@ impl Animation for GlideWieldAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -108,208 +110,215 @@ impl Animation for GlideWieldAnimation { 0.0 } * 1.3; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -2.0 - skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + 15.0, ); - next.l_hand.ori = Quaternion::rotation_x(3.35); + next.l_hand.orientation = Quaternion::rotation_x(3.35); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( 2.0 + skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + 15.0, ); - next.r_hand.ori = Quaternion::rotation_x(3.35); + next.r_hand.orientation = Quaternion::rotation_x(3.35); next.r_hand.scale = Vec3::one(); if speed > 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + short * 0.1, ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) - * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) + * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 2.0 + shortalt * -1.5 - lower, ); - next.chest.ori = Quaternion::rotation_z(short * 0.10 * walkintensity + tilt * -1.0) - * Quaternion::rotation_y(tilt * 2.2) - * Quaternion::rotation_x( - shortalter * 0.035 + wave_stop * speed * -0.1 + (tilt.abs()), - ); + next.chest.orientation = + Quaternion::rotation_z(short * 0.10 * walkintensity + tilt * -1.0) + * Quaternion::rotation_y(tilt * 2.2) + * Quaternion::rotation_x( + shortalter * 0.035 + wave_stop * speed * -0.1 + (tilt.abs()), + ); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5); next.belt.scale = Vec3::one(); - next.glider.ori = Quaternion::rotation_x(0.8); - next.glider.offset = Vec3::new(0.0, -10.0, 15.0); + next.glider.orientation = Quaternion::rotation_x(0.8); + next.glider.position = Vec3::new(0.0, -10.0, 15.0); next.glider.scale = Vec3::one() * 1.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7); next.shorts.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothoril * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertl * -2.7).max(-1.0)) * walkintensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.l_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothorir * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertr * -2.7).max(-1.0)) * walkintensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.r_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); next.r_shoulder.scale = Vec3::one() * 1.1; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.3 + breathe * -0.05, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + slow * 0.3, ); - next.chest.ori = Quaternion::rotation_z(head_look.x * 0.6); + next.chest.orientation = Quaternion::rotation_z(head_look.x * 0.6); next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(head_look.x * -0.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(head_look.x * -0.1); next.belt.scale = Vec3::one() + breathe * -0.03; - next.glider.ori = Quaternion::rotation_x(0.35); - next.glider.offset = Vec3::new(0.0, -9.0, 17.0); + next.glider.orientation = Quaternion::rotation_x(0.35); + next.glider.position = Vec3::new(0.0, -9.0, 17.0); next.glider.scale = Vec3::one() * 1.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(head_look.x * -0.2); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(head_look.x * -0.2); next.shorts.scale = Vec3::one() + breathe * -0.03; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, @@ -318,50 +327,52 @@ impl Animation for GlideWieldAnimation { match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/gliding.rs b/voxygen/src/anim/src/character/gliding.rs index 27d8d3a6f6..738eec1ee4 100644 --- a/voxygen/src/anim/src/character/gliding.rs +++ b/voxygen/src/anim/src/character/gliding.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct GlidingAnimation; @@ -56,7 +58,7 @@ impl Animation for GlidingAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.0001 && m.is_finite()) .reduce_and() @@ -74,77 +76,78 @@ impl Animation for GlidingAnimation { anim_time as f32 }; - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_x(0.35 - slow * 0.10 + head_look.y) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_x(0.35 - slow * 0.10 + head_look.y) * Quaternion::rotation_z(head_look.x + slowa * 0.15); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(slowa * 0.02); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(slowa * 0.02); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slowa * 0.1 + tilt * tiltcancel * 12.0); + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = Quaternion::rotation_z(slowa * 0.1 + tilt * tiltcancel * 12.0); - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(slowa * 0.12 + tilt * tiltcancel * 16.0); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(slowa * 0.12 + tilt * tiltcancel * 16.0); - next.l_hand.offset = Vec3::new(-9.5, -3.0, 10.0); - next.l_hand.ori = Quaternion::rotation_x(-2.7 + slowa * -0.1); + next.l_hand.position = Vec3::new(-9.5, -3.0, 10.0); + next.l_hand.orientation = Quaternion::rotation_x(-2.7 + slowa * -0.1); - next.r_hand.offset = Vec3::new(9.5, -3.0, 10.0); - next.r_hand.ori = Quaternion::rotation_x(-2.7 + slowa * -0.10); + next.r_hand.position = Vec3::new(9.5, -3.0, 10.0); + next.r_hand.orientation = Quaternion::rotation_x(-2.7 + slowa * -0.10); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + slowa * -1.0 + tilt * tiltcancel * -35.0, -1.0 + skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x( + next.l_foot.orientation = Quaternion::rotation_x( (wave_stop * -0.7 - quicka * -0.21 + slow * 0.19) * speed * 0.04, ) * Quaternion::rotation_z(tilt * tiltcancel * 20.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + slowa * 1.0 + tilt * tiltcancel * 35.0, -1.0 + skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x( + next.r_foot.orientation = Quaternion::rotation_x( (wave_stop * -0.8 + quick * -0.25 + slowb * 0.13) * speed * 0.04, ) * Quaternion::rotation_z(tilt * tiltcancel * 20.0); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, -13.0 + slow * 0.10, 8.0); - next.glider.ori = Quaternion::rotation_x(0.8) * Quaternion::rotation_y(slowa * 0.04); + next.glider.position = Vec3::new(0.0, -13.0 + slow * 0.10, 8.0); + next.glider.orientation = + Quaternion::rotation_x(0.8) * Quaternion::rotation_y(slowa * 0.04); next.glider.scale = Vec3::one(); match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } @@ -152,15 +155,16 @@ impl Animation for GlidingAnimation { next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -4.0, 10.0) / 11.0 * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.06 * speed.max(12.0) + slow * 0.04) + next.torso.position = Vec3::new(0.0, -4.0, 10.0) / 11.0 * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.06 * speed.max(12.0) + slow * 0.04) * Quaternion::rotation_y(tilt * tiltcancel * 32.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; diff --git a/voxygen/src/anim/src/character/idle.rs b/voxygen/src/anim/src/character/idle.rs index 7933f4a18a..9b76637dec 100644 --- a/voxygen/src/anim/src/character/idle.rs +++ b/voxygen/src/anim/src/character/idle.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct IdleAnimation; @@ -26,7 +28,7 @@ impl Animation for IdleAnimation { let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI / 2.0).sin(); let head_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1 + wave_ultra_slow * 0.1 + head_abs * -0.5, @@ -34,139 +36,141 @@ impl Animation for IdleAnimation { next.head.scale = Vec3::one() * skeleton_attr.head_scale - head_abs * 0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + wave_ultra_slow * 0.1, ); next.chest.scale = Vec3::one() + head_abs * 0.05; - next.belt.offset = Vec3::new( + next.belt.position = Vec3::new( 0.0, skeleton_attr.belt.0, skeleton_attr.belt.1 + wave_ultra_slow * 0.1, ); - next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.orientation = Quaternion::rotation_x(0.0); next.belt.scale = Vec3::one() - head_abs * 0.05; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1 + wave_ultra_slow * 0.1, ); - next.shorts.ori = Quaternion::rotation_x(0.0); + next.shorts.orientation = Quaternion::rotation_x(0.0); next.shorts.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + wave_ultra_slow_cos * 0.15, skeleton_attr.hand.2 + wave_ultra_slow * 0.5, ); - next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.l_hand.orientation = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 + wave_ultra_slow_cos * 0.15, skeleton_attr.hand.2 + wave_ultra_slow * 0.5 + head_abs * -0.05, ); - next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.r_hand.orientation = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); next.r_hand.scale = Vec3::one() + head_abs * -0.05; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = (Vec3::one() + head_abs * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = (Vec3::one() + head_abs * -0.05) * 1.15; next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0); - next.lantern.scale = Vec3::one() * 0.0; + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/jump.rs b/voxygen/src/anim/src/character/jump.rs index b00ab37dfe..1506f87017 100644 --- a/voxygen/src/anim/src/character/jump.rs +++ b/voxygen/src/anim/src/character/jump.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct JumpAnimation; impl Animation for JumpAnimation { @@ -41,7 +43,7 @@ impl Animation for JumpAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -53,163 +55,165 @@ impl Animation for JumpAnimation { 0.0 } * 1.3; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, -1.0 + skeleton_attr.head.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_x(0.25 + slow * 0.04) * Quaternion::rotation_z(tilt * -2.5); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 1.0); - next.chest.ori = Quaternion::rotation_z(tilt * -2.0); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 1.0); + next.chest.orientation = Quaternion::rotation_z(tilt * -2.0); next.chest.scale = Vec3::one() * 1.01; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(tilt * 2.0); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(tilt * 2.0); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_z(0.0); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_z(0.0); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(tilt * 3.0); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(tilt * 3.0); next.shorts.scale = Vec3::one(); if random > 0.5 { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, 1.0 + skeleton_attr.hand.1 + 4.0, 2.0 + skeleton_attr.hand.2 + slow * 1.5, ); - next.l_hand.ori = + next.l_hand.orientation = Quaternion::rotation_x(1.9 + slow * 0.4) * Quaternion::rotation_y(0.2); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 - 3.0, skeleton_attr.hand.2 + slow * 1.5, ); - next.r_hand.ori = + next.r_hand.orientation = Quaternion::rotation_x(-0.5 + slow * -0.4) * Quaternion::rotation_y(-0.2); next.r_hand.scale = Vec3::one(); } else { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 - 3.0, skeleton_attr.hand.2 + slow * 1.5, ); - next.l_hand.ori = + next.l_hand.orientation = Quaternion::rotation_x(-0.5 + slow * -0.4) * Quaternion::rotation_y(0.2); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, 1.0 + skeleton_attr.hand.1 + 4.0, 2.0 + skeleton_attr.hand.2 + slow * 1.5, ); - next.r_hand.ori = + next.r_hand.orientation = Quaternion::rotation_x(1.9 + slow * 0.4) * Quaternion::rotation_y(-0.2); next.r_hand.scale = Vec3::one(); }; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 - 6.0 * switch, 1.0 + skeleton_attr.foot.2 + slow * 1.5, ); - next.l_foot.ori = Quaternion::rotation_x(-1.2 * switch + slow * -0.2 * switch); + next.l_foot.orientation = Quaternion::rotation_x(-1.2 * switch + slow * -0.2 * switch); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + 6.0 * switch, 1.0 + skeleton_attr.foot.2 + slow * 1.5, ); - next.r_foot.ori = Quaternion::rotation_x(1.2 * switch + slow * 0.2 * switch); + next.r_foot.orientation = Quaternion::rotation_x(1.2 * switch + slow * 0.2 * switch); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.4 * switch); + next.l_shoulder.orientation = Quaternion::rotation_x(0.4 * switch); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(-0.4 * switch); + next.r_shoulder.orientation = Quaternion::rotation_x(-0.4 * switch); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(1.0 * switch + slow * 0.3 * switch) + next.lantern.orientation = Quaternion::rotation_x(1.0 * switch + slow * 0.3 * switch) * Quaternion::rotation_y(0.6 * switch + slow * 0.3 * switch); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/leapmelee.rs b/voxygen/src/anim/src/character/leapmelee.rs index 4c9aca309d..dada5b978d 100644 --- a/voxygen/src/anim/src/character/leapmelee.rs +++ b/voxygen/src/anim/src/character/leapmelee.rs @@ -1,7 +1,7 @@ use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; use common::comp::item::{Hands, ToolKind}; /* use std::f32::consts::PI; */ -use vek::*; +use super::super::vek::*; pub struct LeapAnimation; @@ -32,72 +32,75 @@ impl Animation for LeapAnimation { * ((anim_time as f32 * lab as f32 * 4.0).sin()); if let Some(ToolKind::Hammer(_)) = active_tool_kind { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(3.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(3.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + slower * -1.0, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(slower * 0.05) + next.head.orientation = Quaternion::rotation_z(slower * 0.05) * Quaternion::rotation_x((slowersmooth * -0.25 + slower * 0.55).max(-0.2)) * Quaternion::rotation_y(slower * 0.05); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slower * 0.08 + slowersmooth * 0.15) + next.chest.position = Vec3::new(0.0, 0.0, 7.0); + next.chest.orientation = Quaternion::rotation_z(slower * 0.08 + slowersmooth * 0.15) * Quaternion::rotation_x(-0.3 + slower * 0.45 + slowersmooth * 0.26) * Quaternion::rotation_y(slower * 0.18 + slowersmooth * 0.15); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0 + slower * -0.7); - next.belt.ori = Quaternion::rotation_z(slower * -0.16 + slowersmooth * -0.12) + next.belt.position = Vec3::new(0.0, 0.0, -2.0 + slower * -0.7); + next.belt.orientation = Quaternion::rotation_z(slower * -0.16 + slowersmooth * -0.12) * Quaternion::rotation_x(0.0 + slower * -0.06) * Quaternion::rotation_y(slower * -0.05); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0 + slower * -0.7); - next.shorts.ori = Quaternion::rotation_z(slower * -0.08 + slowersmooth * -0.08) + next.shorts.position = Vec3::new(0.0, 0.0, -5.0 + slower * -0.7); + next.shorts.orientation = Quaternion::rotation_z(slower * -0.08 + slowersmooth * -0.08) * Quaternion::rotation_x(0.0 + slower * -0.08 + slowersmooth * -0.08) * Quaternion::rotation_y(slower * -0.07); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(slower * -0.7 + 0.4) * Quaternion::rotation_y(slower * 0.4); + next.hold.scale = Vec3::one() * 0.0; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, slower * 3.0 + slowersmooth * -6.0 - 2.0, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slower * -0.2 + slowersmooth * -0.3 - 0.2); + next.l_foot.orientation = + Quaternion::rotation_x(slower * -0.2 + slowersmooth * -0.3 - 0.2); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, slower * 2.0 + slowersmooth * -4.0 - 1.0, -2.0 + skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slower * -0.4 + slowersmooth * -0.6 - 1.0); + next.r_foot.orientation = + Quaternion::rotation_x(slower * -0.4 + slowersmooth * -0.6 - 1.0); next.control.scale = Vec3::one(); - next.control.offset = Vec3::new(-7.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(-0.7 + slower * 1.5) + next.control.position = Vec3::new(-7.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(-0.7 + slower * 1.5) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(1.4 + slowersmooth * -0.4 + slower * 0.2); next.control.scale = Vec3::one(); } - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); @@ -110,8 +113,8 @@ impl Animation for LeapAnimation { (_, _) => Vec3::zero(), }; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next } diff --git a/voxygen/src/anim/src/character/mod.rs b/voxygen/src/anim/src/character/mod.rs index 7f2ef17a49..4709eac25b 100644 --- a/voxygen/src/anim/src/character/mod.rs +++ b/voxygen/src/anim/src/character/mod.rs @@ -36,120 +36,79 @@ pub use self::{ swimwield::SwimWieldAnimation, wield::WieldAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp; -use vek::{Vec3, Vec4}; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct CharacterSkeleton { - head: Bone, - chest: Bone, - belt: Bone, - back: Bone, - shorts: Bone, - l_hand: Bone, - r_hand: Bone, - l_foot: Bone, - r_foot: Bone, - l_shoulder: Bone, - r_shoulder: Bone, - glider: Bone, - main: Bone, - second: Bone, - lantern: Bone, - hold: Bone, - torso: Bone, - control: Bone, - l_control: Bone, - r_control: Bone, -} - -impl CharacterSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct CharacterSkeleton { + + head, + + chest, + + belt, + + back, + + shorts, + + l_hand, + + r_hand, + + l_foot, + + r_foot, + + l_shoulder, + + r_shoulder, + + glider, + + main, + + second, + + lantern, + + hold, + torso, + control, + l_control, + r_control, +}); impl Skeleton for CharacterSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 16; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"character_compute_mats\0"; - fn bone_count(&self) -> usize { 16 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "character_compute_mats")] - fn compute_matrices_inner(&self) -> ([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(); - let r_hand_mat = self.r_hand.compute_base_matrix(); - let control_mat = self.control.compute_base_matrix(); - let l_control_mat = self.l_control.compute_base_matrix(); - let r_control_mat = self.r_control.compute_base_matrix(); - let main_mat = self.main.compute_base_matrix(); - let second_mat = self.second.compute_base_matrix(); - let shorts_mat = self.shorts.compute_base_matrix(); - let head_mat = self.head.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); + let chest_mat = torso_mat * Mat4::::from(self.chest); + let head_mat = chest_mat * Mat4::::from(self.head); + let shorts_mat = chest_mat * Mat4::::from(self.shorts); + let control_mat = chest_mat * Mat4::::from(self.control); + let l_control_mat = control_mat * Mat4::::from(self.l_control); + let r_control_mat = control_mat * Mat4::::from(self.r_control); - let lantern_final_mat = - torso_mat * chest_mat * shorts_mat * self.lantern.compute_base_matrix(); + let l_hand_mat = Mat4::::from(self.l_hand); + let lantern_mat = Mat4::::from(self.lantern); - ( - [ - 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 * chest_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), - FigureBoneData::new( - torso_mat - * chest_mat - * control_mat - * l_hand_mat - * self.hold.compute_base_matrix(), - ), - ], - (lantern_final_mat * Vec4::new(0.0, 0.0, 0.0, 1.0)).xyz(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.chest.interpolate(&target.chest, dt); - self.belt.interpolate(&target.belt, dt); - self.back.interpolate(&target.back, dt); - self.shorts.interpolate(&target.shorts, dt); - self.l_hand.interpolate(&target.l_hand, dt); - self.r_hand.interpolate(&target.r_hand, dt); - self.l_foot.interpolate(&target.l_foot, dt); - self.r_foot.interpolate(&target.r_foot, dt); - self.l_shoulder.interpolate(&target.l_shoulder, dt); - self.r_shoulder.interpolate(&target.r_shoulder, dt); - self.glider.interpolate(&target.glider, dt); - self.main.interpolate(&target.main, dt); - self.second.interpolate(&target.second, dt); - self.lantern.interpolate(&target.lantern, dt); - self.hold.interpolate(&target.hold, dt); - self.torso.interpolate(&target.torso, dt); - self.control.interpolate(&target.control, dt); - self.l_control.interpolate(&target.l_control, dt); - self.r_control.interpolate(&target.r_control, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_mat), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.belt)), + make_bone(chest_mat * Mat4::::from(self.back)), + make_bone(shorts_mat), + make_bone(l_control_mat * l_hand_mat), + make_bone(r_control_mat * Mat4::::from(self.r_hand)), + make_bone(torso_mat * Mat4::::from(self.l_foot)), + make_bone(torso_mat * Mat4::::from(self.r_foot)), + make_bone(chest_mat * Mat4::::from(self.l_shoulder)), + make_bone(chest_mat * Mat4::::from(self.r_shoulder)), + make_bone(chest_mat * Mat4::::from(self.glider)), + make_bone(l_control_mat * Mat4::::from(self.main)), + make_bone(r_control_mat * Mat4::::from(self.second)), + make_bone(shorts_mat * lantern_mat), + // FIXME: Should this be l_control_mat? + make_bone(control_mat * l_hand_mat * Mat4::::from(self.hold)), + ]; + // NOTE: lantern_mat.cols.w = lantern_mat * Vec4::unit_w() + (head_mat * lantern_mat.cols.w).xyz() } } diff --git a/voxygen/src/anim/src/character/roll.rs b/voxygen/src/anim/src/character/roll.rs index f5eaf819f3..1db19b1381 100644 --- a/voxygen/src/anim/src/character/roll.rs +++ b/voxygen/src/anim/src/character/roll.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct RollAnimation; @@ -33,7 +35,7 @@ impl Animation for RollAnimation { let spin = anim_time as f32; let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.0001 && m.is_finite()) .reduce_and() @@ -45,141 +47,143 @@ impl Animation for RollAnimation { 0.0 }; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + 3.0, skeleton_attr.head.1 - 1.0, ); - next.head.ori = Quaternion::rotation_x(-0.75); + next.head.orientation = Quaternion::rotation_x(-0.75); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, -9.5 + skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_x(-0.2); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, -9.5 + skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_x(-0.2); next.chest.scale = Vec3::one() * 1.01; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1 + 1.0); - next.belt.ori = Quaternion::rotation_x(0.55); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + 1.0, skeleton_attr.belt.1 + 1.0); + next.belt.orientation = Quaternion::rotation_x(0.55); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + 4.5, skeleton_attr.shorts.1 + 2.5, ); - next.shorts.ori = Quaternion::rotation_x(0.8); + next.shorts.orientation = Quaternion::rotation_x(0.8); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + 1.0, skeleton_attr.hand.2 + 2.0, ); - next.l_hand.ori = Quaternion::rotation_x(0.6); + next.l_hand.orientation = Quaternion::rotation_x(0.6); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, skeleton_attr.hand.1 + 1.0, skeleton_attr.hand.2 + 2.0, ); - next.r_hand.ori = Quaternion::rotation_x(0.6); + next.r_hand.orientation = Quaternion::rotation_x(0.6); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( 1.0 - skeleton_attr.foot.0, skeleton_attr.foot.1 + 5.5, skeleton_attr.foot.2 - 5.0, ); - next.l_foot.ori = Quaternion::rotation_x(0.9); + next.l_foot.orientation = Quaternion::rotation_x(0.9); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + 5.5, skeleton_attr.foot.2 - 5.0, ); - next.r_foot.ori = Quaternion::rotation_x(0.9); + next.r_foot.orientation = Quaternion::rotation_x(0.9); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1 + 2.0, skeleton_attr.shoulder.2 + 1.0, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, 0.0, 8.0) / 11.0 * skeleton_attr.scaler; - next.torso.ori = + next.torso.position = Vec3::new(0.0, 0.0, 8.0) / 11.0 * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(spin * -10.0) * Quaternion::rotation_z(tilt * -10.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/run.rs b/voxygen/src/anim/src/character/run.rs index d821576b11..10ad471fd4 100644 --- a/voxygen/src/anim/src/character/run.rs +++ b/voxygen/src/anim/src/character/run.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct RunAnimation; @@ -98,7 +100,7 @@ impl Animation for RunAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -110,147 +112,151 @@ impl Animation for RunAnimation { 0.0 } * 1.3; - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + short * 0.1, ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) - * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.1) + * Quaternion::rotation_x(head_look.y + 0.45 - lower * 0.35); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + 2.0 + shortalt * -1.5 - lower, ); - next.chest.ori = Quaternion::rotation_z(short * 0.18 * walkintensity + tilt * -0.6) + next.chest.orientation = Quaternion::rotation_z(short * 0.18 * walkintensity + tilt * -0.6) * Quaternion::rotation_y(tilt * 1.6) * Quaternion::rotation_x( impact * 0.06 + shortalter * 0.035 + wave_stop * speed * -0.07 + (tilt.abs()), ); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = + Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(short * 0.25 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0 + foothorir * -1.3, 3.0 + skeleton_attr.hand.1 + foothorir * -7.0 * walkintensity, 1.5 + skeleton_attr.hand.2 - foothorir * 5.5 * walkintensity, ); - next.l_hand.ori = Quaternion::rotation_x(0.6 + footrotr * -1.2 * walkintensity) + next.l_hand.orientation = Quaternion::rotation_x(0.6 + footrotr * -1.2 * walkintensity) * Quaternion::rotation_y(footrotr * 0.4 * walkintensity); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0 + foothoril * 1.3, 3.0 + skeleton_attr.hand.1 + foothoril * -6.5 * walkintensity, 1.5 + skeleton_attr.hand.2 - foothoril * 7.0 * walkintensity, ); - next.r_hand.ori = Quaternion::rotation_x(0.6 + footrotl * -1.2 * walkintensity) + next.r_hand.orientation = Quaternion::rotation_x(0.6 + footrotl * -1.2 * walkintensity) * Quaternion::rotation_y(footrotl * -0.4 * walkintensity); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothoril * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertl * -2.7).max(-1.0)) * walkintensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) + next.l_foot.orientation = Quaternion::rotation_x(-0.2 + footrotl * -1.2 * walkintensity) * Quaternion::rotation_y(tilt * 1.8); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, -1.5 + skeleton_attr.foot.1 + foothorir * -8.5 * walkintensity - lower * 1.0, 2.0 + skeleton_attr.foot.2 + ((footvertr * -2.7).max(-1.0)) * walkintensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) + next.r_foot.orientation = Quaternion::rotation_x(-0.2 + footrotr * -1.2 * walkintensity) * Quaternion::rotation_y(tilt * 1.8); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.7 + 0.4) * Quaternion::rotation_y(shorte * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, -0.3, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/shoot.rs b/voxygen/src/anim/src/character/shoot.rs index 9184ab99f5..1fe4dfc856 100644 --- a/voxygen/src/anim/src/character/shoot.rs +++ b/voxygen/src/anim/src/character/shoot.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; -use vek::*; pub struct ShootAnimation; @@ -36,71 +38,73 @@ impl Animation for ShootAnimation { let exp = ((anim_time as f32).powf(0.3 as f32)).min(1.2); - next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(exp * -0.4) + next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(exp * -0.4) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(exp * 0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 - exp * 1.5, skeleton_attr.chest.1, ); - next.chest.ori = Quaternion::rotation_z(0.4 + exp * 1.0) + next.chest.orientation = Quaternion::rotation_z(0.4 + exp * 1.0) * Quaternion::rotation_x(0.0 + exp * 0.2) * Quaternion::rotation_y(exp * -0.08); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + exp * 1.0, skeleton_attr.belt.1); - next.belt.ori = next.chest.ori * -0.1; + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0 + exp * 1.0, skeleton_attr.belt.1); + next.belt.orientation = next.chest.orientation * -0.1; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + exp * 1.0, skeleton_attr.shorts.1, ); - next.shorts.ori = next.chest.ori * -0.08; + next.shorts.orientation = next.chest.orientation * -0.08; match active_tool_kind { //TODO: Inventory Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.5, 0.5, -4.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.5, 0.5, -4.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(8.0, 4.0, 2.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(8.0, 4.0, 2.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(9.2, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(9.2, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0 - exp * 5.0); - next.control.ori = Quaternion::rotation_x(exp * 1.3) + next.control.position = Vec3::new(-7.0, 6.0, 6.0 - exp * 5.0); + next.control.orientation = Quaternion::rotation_x(exp * 1.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(exp * 1.5); next.control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(1.0 - exp * 2.0, -4.0 - exp * 4.0, -1.0 + exp * 6.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = + Vec3::new(1.0 - exp * 2.0, -4.0 - exp * 4.0, -1.0 + exp * 6.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6 + exp * 0.8) * Quaternion::rotation_z(-0.3 + exp * 0.9); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(4.9, 3.0, -4.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(4.9, 3.0, -4.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.control.offset = Vec3::new(-9.0, 6.0, 8.0); - next.control.ori = Quaternion::rotation_x(exp * 0.4) + next.control.position = Vec3::new(-9.0, 6.0, 8.0); + next.control.orientation = Quaternion::rotation_x(exp * 0.4) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.control.scale = Vec3::one(); @@ -108,81 +112,82 @@ impl Animation for ShootAnimation { _ => {}, } if velocity > 0.5 { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0 - foot * 1.0 + exp * -1.0, foote * 0.8 + exp * 1.5, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(exp * 0.5) + next.l_foot.orientation = Quaternion::rotation_x(exp * 0.5) * Quaternion::rotation_z(exp * 0.4) * Quaternion::rotation_y(0.15); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0 + foot * 1.0 + exp * 1.0, foote * -0.8 + exp * -1.0, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(exp * -0.5) + next.r_foot.orientation = Quaternion::rotation_x(exp * -0.5) * Quaternion::rotation_z(exp * 0.4) * Quaternion::rotation_y(0.0); next.r_foot.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.15); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.15); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.5, skeleton_attr.foot.2 + exp * 2.5, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(exp * -0.2 - 0.2) * Quaternion::rotation_z(exp * 1.0); - next.r_foot.offset = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 3.5 - exp * 2.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(exp * 0.1) * Quaternion::rotation_z(exp * 0.5); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(0.0); + next.r_foot.orientation = + Quaternion::rotation_x(exp * 0.1) * Quaternion::rotation_z(exp * 0.5); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.back.offset = Vec3::new(0.0, -2.8, 7.25); - next.back.ori = Quaternion::rotation_x(-0.3); + next.back.position = Vec3::new(0.0, -2.8, 7.25); + next.back.orientation = Quaternion::rotation_x(-0.3); next.back.scale = Vec3::one() * 1.02; - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.position = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.position = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 5.0, 0.0); - next.glider.ori = Quaternion::rotation_y(0.0); + next.glider.position = Vec3::new(0.0, 5.0, 0.0); + next.glider.orientation = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(exp * -0.7 + 0.4) * Quaternion::rotation_y(exp * 0.4); next.lantern.scale = Vec3::one() * 0.65; - next.hold.offset = Vec3::new(17.5, -25.0, -10.5); - next.hold.ori = Quaternion::rotation_x(-1.6) + next.hold.position = Vec3::new(17.5, -25.0, -10.5); + next.hold.orientation = Quaternion::rotation_x(-1.6) * Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.0); next.hold.scale = Vec3::one() * 0.0; - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/sit.rs b/voxygen/src/anim/src/character/sit.rs index 268e7e8dd7..f7d61a1e08 100644 --- a/voxygen/src/anim/src/character/sit.rs +++ b/voxygen/src/anim/src/character/sit.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SitAnimation; @@ -39,133 +41,136 @@ impl Animation for SitAnimation { .sin() * 0.125, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.1 + stop * -0.8, ); - next.head.ori = Quaternion::rotation_z(head_look.x + slow * 0.2 - slow * 0.1) + next.head.orientation = Quaternion::rotation_z(head_look.x + slow * 0.2 - slow * 0.1) * Quaternion::rotation_x((slowa * -0.1 + slow * 0.1 + head_look.y).abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 + stop * -0.4, skeleton_attr.chest.1 + slow * 0.1 + stop * -0.8, ); - next.chest.ori = Quaternion::rotation_x(stop * 0.15); + next.chest.orientation = Quaternion::rotation_x(stop * 0.15); next.chest.scale = Vec3::one() + slow_abs * 0.05; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0 + stop * 1.2, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(stop * 0.3); + next.belt.position = + Vec3::new(0.0, skeleton_attr.belt.0 + stop * 1.2, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_x(stop * 0.3); next.belt.scale = (Vec3::one() + slow_abs * 0.05) * 1.02; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + stop * 2.5, skeleton_attr.shorts.1 + stop * 0.6, ); - next.shorts.ori = Quaternion::rotation_x(stop * 0.6); + next.shorts.orientation = Quaternion::rotation_x(stop * 0.6); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + slowa * 0.15, skeleton_attr.hand.2 + slow * 0.7 + stop * -2.0, ); - next.l_hand.ori = Quaternion::rotation_x(slowa * -0.1 + slow * 0.1); + next.l_hand.orientation = Quaternion::rotation_x(slowa * -0.1 + slow * 0.1); next.l_hand.scale = Vec3::one() + slow_abs * -0.05; - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 + slowa * 0.15, skeleton_attr.hand.2 + slow * 0.7 + stop * -2.0, ); - next.r_hand.ori = Quaternion::rotation_x(slow * -0.1 + slowa * 0.1); + next.r_hand.orientation = Quaternion::rotation_x(slow * -0.1 + slowa * 0.1); next.r_hand.scale = Vec3::one() + slow_abs * -0.05; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, 3.0 + skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(slow * 0.1 + stop * 1.2 + slow * 0.1); + next.l_foot.orientation = Quaternion::rotation_x(slow * 0.1 + stop * 1.2 + slow * 0.1); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, 3.0 + skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(slowa * 0.1 + stop * 1.2 + slowa * 0.1); + next.r_foot.orientation = Quaternion::rotation_x(slowa * 0.1 + stop * 1.2 + slowa * 0.1); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = (Vec3::one() + slow_abs * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = (Vec3::one() + slow_abs * -0.05) * 1.15; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, stop * -0.16) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, -0.2, stop * -0.16) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/sneak.rs b/voxygen/src/anim/src/character/sneak.rs index 52563090a1..83b44d82f3 100644 --- a/voxygen/src/anim/src/character/sneak.rs +++ b/voxygen/src/anim/src/character/sneak.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::ToolKind; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SneakAnimation; @@ -74,237 +76,242 @@ impl Animation for SneakAnimation { * 0.1, ); - let ori: Vec2 = Vec2::from(orientation); + let orientation: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(orientation, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() - && ori.angle_between(last_ori).is_finite() + && orientation.angle_between(last_ori).is_finite() { - ori.angle_between(last_ori).min(0.2) - * last_ori.determine_side(Vec2::zero(), ori).signum() + orientation.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), orientation).signum() } else { 0.0 } * 1.3; + next.hold.scale = Vec3::one() * 0.0; + if speed > 0.5 { - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( 1.0 - skeleton_attr.hand.0, 4.0 + skeleton_attr.hand.1, 1.0 + skeleton_attr.hand.2, ); - next.l_hand.ori = Quaternion::rotation_x(1.0); + next.l_hand.orientation = Quaternion::rotation_x(1.0); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, -1.0 + skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.r_hand.ori = Quaternion::rotation_x(0.4); + next.r_hand.orientation = Quaternion::rotation_x(0.4); next.r_hand.scale = Vec3::one(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -4.0 + skeleton_attr.head.0, -1.0 + skeleton_attr.head.1 + short * 0.06, ); - next.head.ori = Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.06) - * Quaternion::rotation_x(head_look.y + 0.45); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.06) + * Quaternion::rotation_x(head_look.y + 0.45); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, -1.0 + skeleton_attr.chest.1 + shortalt * -0.5, ); - next.chest.ori = Quaternion::rotation_z(0.3 + short * 0.08 + tilt * -0.2) + next.chest.orientation = Quaternion::rotation_z(0.3 + short * 0.08 + tilt * -0.2) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_x(-0.5); next.chest.scale = Vec3::one(); - next.belt.offset = + next.belt.position = Vec3::new(0.0, 0.5 + skeleton_attr.belt.0, 0.7 + skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5) * Quaternion::rotation_x(0.2); next.belt.scale = Vec3::one(); - next.glider.ori = Quaternion::rotation_x(0.0); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.orientation = Quaternion::rotation_x(0.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, 1.0 + skeleton_attr.shorts.0, 1.0 + skeleton_attr.shorts.1, ); - next.shorts.ori = Quaternion::rotation_z(short * 0.16 + tilt * -1.5) + next.shorts.orientation = Quaternion::rotation_z(short * 0.16 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7) * Quaternion::rotation_x(0.3); next.shorts.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * -10.5 * walkintensity - lower * 1.0, 1.0 + skeleton_attr.foot.2 + ((footvertl * -1.7).max(-1.0)) * walkintensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.2 + footrotl * -0.8 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.l_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotl * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * -10.5 * walkintensity - lower * 1.0, 1.0 + skeleton_attr.foot.2 + ((footvertr * -1.7).max(-1.0)) * walkintensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.2 + footrotr * -0.8 * walkintensity) - * Quaternion::rotation_y(tilt * 1.8); + next.r_foot.orientation = + Quaternion::rotation_x(-0.2 + footrotr * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * walkintensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * walkintensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); next.r_shoulder.scale = Vec3::one() * 1.1; - next.main.offset = Vec3::new(-7.0, -6.5, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -6.5, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(shorte * 0.2 + 0.4) * Quaternion::rotation_y(shorte * 0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_y(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - next.control.ori = Quaternion::rotation_x(0.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + next.control.orientation = Quaternion::rotation_x(0.0); next.control.scale = Vec3::one(); next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -4.0 + skeleton_attr.head.0, -2.0 + skeleton_attr.head.1 + slow * 0.1 + breathe * -0.05, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(0.6 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, -3.0 + skeleton_attr.chest.1 + slow * 0.1, ); - next.chest.ori = Quaternion::rotation_x(-0.7); + next.chest.orientation = Quaternion::rotation_x(-0.7); next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_z(0.3 + head_look.x * -0.1); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_z(0.3 + head_look.x * -0.1); next.belt.scale = Vec3::one() + breathe * -0.03; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( 1.0 - skeleton_attr.hand.0, 5.0 + skeleton_attr.hand.1, 0.0 + skeleton_attr.hand.2, ); - next.l_hand.ori = Quaternion::rotation_x(1.35); + next.l_hand.orientation = Quaternion::rotation_x(1.35); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( -1.0 + skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.r_hand.ori = Quaternion::rotation_x(0.4); + next.r_hand.orientation = Quaternion::rotation_x(0.4); next.r_hand.scale = Vec3::one(); - next.glider.ori = Quaternion::rotation_x(0.35); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.orientation = Quaternion::rotation_x(0.35); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(0.6 + head_look.x * -0.2); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.6 + head_look.x * -0.2); next.shorts.scale = Vec3::one() + breathe * -0.03; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -6.0 + skeleton_attr.foot.1, 1.0 + skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(-0.5); + next.l_foot.orientation = Quaternion::rotation_x(-0.5); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 4.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(0.0); + next.r_foot.orientation = Quaternion::rotation_x(0.0); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); next.main.scale = Vec3::one(); - next.second.offset = Vec3::new(0.0, 0.0, 0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/spin.rs b/voxygen/src/anim/src/character/spin.rs index 52fa49eef5..8896e71bae 100644 --- a/voxygen/src/anim/src/character/spin.rs +++ b/voxygen/src/anim/src/character/spin.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct Input { pub attack: bool, @@ -43,86 +45,87 @@ impl Animation for SpinAnimation { ) = active_tool_kind { //INTENTION: SWORD - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = Quaternion::rotation_x(1.27); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = Quaternion::rotation_x(1.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(0.0, 6.0, -1.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); - next.control.ori = Quaternion::rotation_x(-1.7) + next.control.position = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); + next.control.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_y(0.2 + spin * -2.0) * Quaternion::rotation_z(1.4 + spin * 0.1); next.control.scale = Vec3::one(); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0 + spin * -0.8, skeleton_attr.head.1, ); - next.head.ori = Quaternion::rotation_z(spin * -0.25) + next.head.orientation = Quaternion::rotation_z(spin * -0.25) * Quaternion::rotation_x(0.0 + spin * -0.1) * Quaternion::rotation_y(spin * -0.2); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(spin * 0.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(spin * 0.1) * Quaternion::rotation_x(0.0 + spin * 0.1) * Quaternion::rotation_y(decel * -0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = next.chest.ori * -0.1; + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = next.chest.orientation * -0.1; next.belt.scale = Vec3::one(); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.belt.ori = next.chest.ori * -0.08; + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.belt.orientation = next.chest.orientation * -0.08; next.shorts.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z((spin * 7.0).max(0.3)) + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z((spin * 7.0).max(0.3)) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } - next.l_foot.offset = Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2); - next.l_foot.ori = Quaternion::rotation_x(foot * -1.2); + next.l_foot.position = Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2); + next.l_foot.orientation = Quaternion::rotation_x(foot * -1.2); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, foot * -1.0, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(foot * 1.2); + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, foot * -1.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(foot * 1.2); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); - next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.position = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.orientation = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); - next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.position = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.orientation = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 5.0, 0.0); - next.glider.ori = Quaternion::rotation_y(0.0); + next.glider.position = Vec3::new(0.0, 5.0, 0.0); + next.glider.orientation = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = + next.lantern.orientation = Quaternion::rotation_x(spin * -0.7 + 0.4) * Quaternion::rotation_y(spin * 0.4); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; - next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.position = Vec3::new(0.0, 0.0, 0.0); + next.l_control.orientation = Quaternion::rotation_x(0.0); next.l_control.scale = Vec3::one(); - next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.position = Vec3::new(0.0, 0.0, 0.0); + next.r_control.orientation = Quaternion::rotation_x(0.0); next.r_control.scale = Vec3::one(); next.second.scale = match ( diff --git a/voxygen/src/anim/src/character/spinmelee.rs b/voxygen/src/anim/src/character/spinmelee.rs index 949b549482..b7c06d9fe4 100644 --- a/voxygen/src/anim/src/character/spinmelee.rs +++ b/voxygen/src/anim/src/character/spinmelee.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::f32::consts::PI; -use vek::*; pub struct SpinMeleeAnimation; @@ -48,111 +50,113 @@ impl Animation for SpinMeleeAnimation { let quick = (anim_time as f32 * lab as f32 * 8.0).sin(); if let Some(ToolKind::Axe(_)) = active_tool_kind { - next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0); - next.l_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0); + next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(PI); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5); - next.r_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.r_hand.position = Vec3::new(0.5, 0.0, -2.5); + next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-0.0, -2.0, -1.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(-0.0, -2.0, -1.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(0.0, 16.0, 3.0); - next.control.ori = Quaternion::rotation_x(-1.4) + next.control.position = Vec3::new(0.0, 16.0, 3.0); + next.control.orientation = Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(1.4); next.control.scale = Vec3::one(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(0.0) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(0.08); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 - 3.0, skeleton_attr.chest.1 - 2.0, ); - next.chest.ori = Quaternion::rotation_z(0.0) + next.chest.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.3); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 1.0, -1.0); - next.belt.ori = Quaternion::rotation_z(0.0) + next.belt.position = Vec3::new(0.0, 1.0, -1.0); + next.belt.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.4) * Quaternion::rotation_y(0.0); next.belt.scale = Vec3::one() * 0.98; - next.shorts.offset = Vec3::new(0.0, 3.0, -2.5); - next.shorts.ori = Quaternion::rotation_z(0.0) + next.shorts.position = Vec3::new(0.0, 3.0, -2.5); + next.shorts.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.7) * Quaternion::rotation_y(0.0); next.shorts.scale = Vec3::one(); - next.torso.offset = Vec3::new( + next.torso.position = Vec3::new( -xshift * (anim_time as f32).min(0.6), -yshift * (anim_time as f32).min(0.6), 0.0, ) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(spin * -16.0) + next.torso.orientation = Quaternion::rotation_z(spin * -16.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } if velocity.z.abs() > 0.1 { - next.l_foot.offset = Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); - next.l_foot.ori = Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0); + next.l_foot.position = + Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); + next.l_foot.orientation = Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); - next.r_foot.ori = Quaternion::rotation_x(1.0); + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0); + next.r_foot.orientation = Quaternion::rotation_x(1.0); next.r_foot.scale = Vec3::one(); } else if speed < 0.5 { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 2.0 + quick * -6.0, skeleton_attr.foot.2, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2); - next.r_foot.ori = + next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2); + next.r_foot.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_y(-0.4); next.r_foot.scale = Vec3::one(); } else { - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, 2.0 + quick * -6.0, skeleton_attr.foot.2, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + quick * 6.0, skeleton_attr.foot.2, ); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_z(0.0); next.r_foot.scale = Vec3::one(); }; - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_z(0.0) + next.lantern.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.7) * Quaternion::rotation_y(-0.8); - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.hold.scale = Vec3::one() * 0.0; + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; next.l_control.scale = Vec3::one(); next.r_control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/stand.rs b/voxygen/src/anim/src/character/stand.rs index bfa97846b3..3362bd4ea2 100644 --- a/voxygen/src/anim/src/character/stand.rs +++ b/voxygen/src/anim/src/character/stand.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct StandAnimation; @@ -37,141 +39,143 @@ impl Animation for StandAnimation { .sin() * 0.15, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.3 + breathe * -0.05, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(impact * -0.02 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale + breathe * -0.05; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + slow * 0.3 + impact * 0.2, ); - next.chest.ori = + next.chest.orientation = Quaternion::rotation_z(head_look.x * 0.6) * Quaternion::rotation_x(impact * 0.04); next.chest.scale = Vec3::one() * 1.01 + breathe * 0.03; - next.belt.offset = Vec3::new( + next.belt.position = Vec3::new( 0.0, skeleton_attr.belt.0 + impact * 0.005, skeleton_attr.belt.1, ); - next.belt.ori = + next.belt.orientation = Quaternion::rotation_z(head_look.x * -0.1) * Quaternion::rotation_x(impact * -0.03); next.belt.scale = Vec3::one() + breathe * -0.03; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new( + next.shorts.position = Vec3::new( 0.0, skeleton_attr.shorts.0 + impact * -0.2, skeleton_attr.shorts.1, ); - next.shorts.ori = + next.shorts.orientation = Quaternion::rotation_z(head_look.x * -0.2) * Quaternion::rotation_x(impact * -0.04); next.shorts.scale = Vec3::one() + breathe * -0.03; - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1 + slow * 0.15 - impact * 0.2, skeleton_attr.hand.2 + slow * 0.5 + impact * -0.1, ); - next.l_hand.ori = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); + next.l_hand.orientation = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1 + slow * 0.15 - impact * 0.2, skeleton_attr.hand.2 + slow * 0.5 + impact * -0.1, ); - next.r_hand.ori = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); + next.r_hand.orientation = Quaternion::rotation_x(slow * -0.06 + impact * -0.1); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 - impact * 0.15, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(impact * 0.02); + next.l_foot.orientation = Quaternion::rotation_x(impact * 0.02); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + impact * 0.15, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(impact * -0.02); + next.r_foot.orientation = Quaternion::rotation_x(impact * -0.02); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); next.r_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; - + next.hold.position = Vec3::new(0.4, -0.3, -5.8); + next.hold.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); + next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1); next.lantern.scale = Vec3::one() * 0.65; - next.torso.offset = Vec3::new(0.0, 0.0, 0.) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/swim.rs b/voxygen/src/anim/src/character/swim.rs index ccb37b6e25..dfaacc2501 100644 --- a/voxygen/src/anim/src/character/swim.rs +++ b/voxygen/src/anim/src/character/swim.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SwimAnimation; @@ -77,7 +79,7 @@ impl Animation for SwimAnimation { ); let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -91,12 +93,12 @@ impl Animation for SwimAnimation { let abstilt = tilt.abs(); let squash = if abstilt > 0.2 { 0.35 } else { 1.0 }; //condenses the body at strong turns - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.0 + skeleton_attr.head.0, skeleton_attr.head.1 - 1.0 + short * 0.3, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x * 0.3 + short * -0.2 * intensity + tilt * 3.0) * Quaternion::rotation_x( (0.4 * head_look.y * (1.0 / intensity)).abs() @@ -106,132 +108,135 @@ impl Animation for SwimAnimation { ); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, -10.0 + skeleton_attr.chest.1 + short * 0.3 * intensity, ); - next.chest.ori = Quaternion::rotation_z(short * 0.1 * intensity); + next.chest.orientation = Quaternion::rotation_z(short * 0.1 * intensity); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) * Quaternion::rotation_z(short * -0.2 * intensity); next.belt.scale = Vec3::one(); - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_x(velocity.z.abs() * -0.005 + abstilt * 1.0) * Quaternion::rotation_z(short * -0.3 * intensity); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( + next.l_hand.position = Vec3::new( -1.0 - skeleton_attr.hand.0, 1.5 + skeleton_attr.hand.1 - foot * 2.0 * intensity * squash, intensity * 5.0 + skeleton_attr.hand.2 + foot * -5.0 * intensity * squash, ); - next.l_hand.ori = Quaternion::rotation_x(1.5 + foot * -1.2 * intensity * squash) + next.l_hand.orientation = Quaternion::rotation_x(1.5 + foot * -1.2 * intensity * squash) * Quaternion::rotation_y(0.4 + foot * -0.35); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( + next.r_hand.position = Vec3::new( 1.0 + skeleton_attr.hand.0, 1.5 + skeleton_attr.hand.1 + foot * 2.0 * intensity * squash, intensity * 5.0 + skeleton_attr.hand.2 + foot * 5.0 * intensity * squash, ); - next.r_hand.ori = Quaternion::rotation_x(1.5 + foot * 1.2 * intensity * squash) + next.r_hand.orientation = Quaternion::rotation_x(1.5 + foot * 1.2 * intensity * squash) * Quaternion::rotation_y(-0.4 + foot * -0.35); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * 1.5 * intensity * squash, -10.0 + skeleton_attr.foot.2 + footrotl * 3.0 * intensity * squash, ); - next.l_foot.ori = + next.l_foot.orientation = Quaternion::rotation_x(-0.8 * squash + footrotl * 0.4 * intensity * squash); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * 1.5 * intensity * squash, -10.0 + skeleton_attr.foot.2 + footrotr * 3.0 * intensity * squash, ); - next.r_foot.ori = + next.r_foot.orientation = Quaternion::rotation_x(-0.8 * squash + footrotr * 0.4 * intensity * squash); next.r_foot.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new( + next.l_shoulder.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15 * intensity); + next.l_shoulder.orientation = Quaternion::rotation_x(short * 0.15 * intensity); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new( + next.r_shoulder.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15 * intensity); + next.r_shoulder.orientation = Quaternion::rotation_x(short * -0.15 * intensity); next.r_shoulder.scale = Vec3::one() * 1.1; - next.glider.offset = Vec3::new(0.0, 0.0, 10.0); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); next.glider.scale = Vec3::one() * 0.0; match active_tool_kind { Some(ToolKind::Dagger(_)) => { - next.main.offset = Vec3::new(-4.0, -5.0, 7.0); - next.main.ori = + next.main.position = Vec3::new(-4.0, -5.0, 7.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.main.offset = Vec3::new(-0.0, -5.0, 3.0); - next.main.ori = + next.main.position = Vec3::new(-0.0, -5.0, 3.0); + next.main.orientation = Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, _ => { - next.main.offset = Vec3::new(-7.0, -5.0, 15.0); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.position = Vec3::new(-7.0, -5.0, 15.0); + next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.main.scale = Vec3::one(); match second_tool_kind { Some(ToolKind::Dagger(_)) => { - next.second.offset = Vec3::new(4.0, -6.0, 7.0); - next.second.ori = + next.second.position = Vec3::new(4.0, -6.0, 7.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(-1.5 * PI); }, Some(ToolKind::Shield(_)) => { - next.second.offset = Vec3::new(0.0, -4.0, 3.0); - next.second.ori = + next.second.position = Vec3::new(0.0, -4.0, 3.0); + next.second.orientation = Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI); }, _ => { - next.second.offset = Vec3::new(-7.0, -5.0, 15.0); - next.second.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.second.position = Vec3::new(-7.0, -5.0, 15.0); + next.second.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); }, } next.second.scale = Vec3::one(); - next.lantern.offset = Vec3::new( + next.lantern.position = Vec3::new( skeleton_attr.lantern.0, skeleton_attr.lantern.1, skeleton_attr.lantern.2, ); - next.lantern.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.lantern.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.lantern.scale = Vec3::one() * 0.65; + next.hold.scale = Vec3::one() * 0.0; + let switch = if avg_vel.z > 0.0 && avgspeed < 0.5 { avgtotal.min(0.5) } else { avgtotal }; - next.torso.offset = Vec3::new(0.0, 0.0, 1.0 - avgspeed * 0.05) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x( + next.torso.position = Vec3::new(0.0, 0.0, 1.0 - avgspeed * 0.05) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x( (((1.0 / switch) * PI / 2.0 + avg_vel.z * 0.12).min(1.57) - PI / 2.0) + avgspeed * avg_vel.z * -0.003, ) * Quaternion::rotation_y(tilt * 8.0) diff --git a/voxygen/src/anim/src/character/swimwield.rs b/voxygen/src/anim/src/character/swimwield.rs index 3554ffa5e5..4e0f2f4b72 100644 --- a/voxygen/src/anim/src/character/swimwield.rs +++ b/voxygen/src/anim/src/character/swimwield.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct SwimWieldAnimation; @@ -67,90 +69,96 @@ impl Animation for SwimWieldAnimation { let noisea = (anim_time as f32 * 11.0 + PI / 6.0).sin(); let noiseb = (anim_time as f32 * 19.0 + PI / 4.0).sin(); - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * 1.5 * intensity, -10.0 + skeleton_attr.foot.2 + footrotl * 3.0 * intensity, ); - next.l_foot.ori = Quaternion::rotation_x(-0.8 + footrotl * 0.4 * intensity); + next.l_foot.orientation = Quaternion::rotation_x(-0.8 + footrotl * 0.4 * intensity); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * 1.5 * intensity, -10.0 + skeleton_attr.foot.2 + footrotr * 3.0 * intensity, ); - next.r_foot.ori = Quaternion::rotation_x(-0.8 + footrotr * 0.4 * intensity); + next.r_foot.orientation = Quaternion::rotation_x(-0.8 + footrotr * 0.4 * intensity); next.r_foot.scale = Vec3::one(); + + next.hold.scale = Vec3::one() * 0.0; + if velocity > 0.01 { - next.torso.offset = Vec3::new(0.0, 0.0, 1.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(velocity * -0.05); + next.torso.position = Vec3::new(0.0, 0.0, 1.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(velocity * -0.05); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x( + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x( (-0.5 + short * 0.3 + noisea * 0.3 + noiseb * 0.3).min(-0.1), ); next.back.scale = Vec3::one() * 1.02; } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0 + slowalt * 0.5, skeleton_attr.chest.0, skeleton_attr.chest.1 + u_slow * 0.5, ); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.chest.ori = + next.chest.orientation = Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.25); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = + Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); next.belt.scale = Vec3::one() * 1.02; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.2); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.2); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.3); } match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -3.0); - next.main.ori = Quaternion::rotation_x(-0.1) + next.main.position = Vec3::new(0.0, 0.0, -3.0); + next.main.orientation = Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.15) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); @@ -160,129 +168,132 @@ impl Animation for SwimWieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - //next.control.ori = Quaternion::rotation_x(slow * 1.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + //next.control.orientation = Quaternion::rotation_x(slow * 1.0); // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { if velocity < 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.5 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(0.35 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.ori = Quaternion::rotation_x(-0.35) + next.chest.orientation = Quaternion::rotation_x(-0.35) * Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.15); - next.belt.offset = + next.belt.position = Vec3::new(0.0, 1.0 + skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(0.15) + next.belt.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.15); - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, 1.0 + skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); - next.control.ori = Quaternion::rotation_x(1.8) + next.shorts.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); + next.control.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } else { - next.control.ori = Quaternion::rotation_x(2.1) + next.control.orientation = Quaternion::rotation_x(2.1) * Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } - next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0); - next.l_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0); + next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5); - next.r_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.r_hand.position = Vec3::new(0.5, 0.0, -2.5); + next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-0.0, -2.0, -1.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(-0.0, -2.0, -1.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-3.0, 11.0, 3.0); + next.control.position = Vec3::new(-3.0, 11.0, 3.0); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = + Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(2.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(2.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.control.offset = Vec3::new(6.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(0.3 + u_slow * 0.15) + next.control.position = Vec3::new(6.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(0.3 + u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.5, 0.5, -4.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.5, 0.5, -4.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(8.0, 4.0, 2.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(8.0, 4.0, 2.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(12.0, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(12.0, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new(-14.0, 1.8, 3.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.2) + next.control.position = Vec3::new(-14.0, 1.8, 3.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_y(-0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); @@ -292,116 +303,117 @@ impl Animation for SwimWieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(2.0, 1.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(2.0, 1.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(5.9, 4.5, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(5.9, 4.5, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.hold.offset = Vec3::new(1.2, -1.0, -5.2); - next.hold.ori = Quaternion::rotation_x(-1.7) + next.hold.position = Vec3::new(1.2, -1.0, -5.2); + next.hold.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.1); next.hold.scale = Vec3::one() * 1.0; - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0); - next.l_hand.ori = Quaternion::rotation_x(1.27) + next.l_hand.position = Vec3::new(-7.0, 4.0, 3.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, -1.25); - next.r_hand.ori = Quaternion::rotation_x(1.27) + next.r_hand.position = Vec3::new(7.0, 2.5, -1.25); + next.r_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, -2.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, -2.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.27) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(0.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Farming(_)) => { if velocity < 0.5 { - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(-0.2 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; } - next.l_hand.offset = Vec3::new(9.0, 1.0, 1.0); - next.l_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(9.0, 1.0, 1.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 11.0); - next.r_hand.ori = Quaternion::rotation_x(1.57) + next.r_hand.position = Vec3::new(9.0, 1.0, 11.0); + next.r_hand.orientation = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(7.5, 7.5, 13.2); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(7.5, 7.5, 13.2); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(3.14) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.1) + next.control.position = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.1) * Quaternion::rotation_y(0.6 + u_slow * 0.1) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/character/wield.rs b/voxygen/src/anim/src/character/wield.rs index 0251f9bb8f..375ce073b7 100644 --- a/voxygen/src/anim/src/character/wield.rs +++ b/voxygen/src/anim/src/character/wield.rs @@ -1,7 +1,9 @@ -use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; use common::comp::item::{Hands, ToolKind}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct WieldAnimation; @@ -51,76 +53,79 @@ impl Animation for WieldAnimation { let noiseb = (anim_time as f32 * 19.0 + PI / 4.0).sin(); if velocity > 0.5 { - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x( + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x( (-0.5 + short * 0.3 + noisea * 0.3 + noiseb * 0.3).min(-0.1), ); next.back.scale = Vec3::one() * 1.02; } else { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0 + slowalt * 0.5, skeleton_attr.chest.0, skeleton_attr.chest.1 + u_slow * 0.5, ); - next.torso.offset = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.l_foot.offset = Vec3::new( + next.l_foot.position = Vec3::new( -skeleton_attr.foot.0, -2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.l_foot.ori = Quaternion::rotation_x(u_slowalt * 0.035 - 0.2); + next.l_foot.orientation = Quaternion::rotation_x(u_slowalt * 0.035 - 0.2); - next.r_foot.offset = Vec3::new( + next.r_foot.position = Vec3::new( skeleton_attr.foot.0, 2.0 + skeleton_attr.foot.1, skeleton_attr.foot.2, ); - next.r_foot.ori = Quaternion::rotation_x(u_slow * 0.035); + next.r_foot.orientation = Quaternion::rotation_x(u_slow * 0.035); - next.chest.ori = + next.chest.orientation = Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.15); - next.belt.offset = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); + next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1); + next.belt.orientation = + Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.22); next.belt.scale = Vec3::one() * 1.02; - next.back.offset = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); - next.back.ori = Quaternion::rotation_x(-0.2); + next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1); + next.back.orientation = Quaternion::rotation_x(-0.2); next.back.scale = Vec3::one() * 1.02; - next.shorts.offset = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_z(0.3); + next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.3); } match active_tool_kind { //TODO: Inventory Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); + next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); + next.r_hand.position = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 0.0, -3.0); - next.main.ori = Quaternion::rotation_x(-0.1) + next.main.position = Vec3::new(0.0, 0.0, -3.0); + next.main.orientation = Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.15) + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); @@ -130,129 +135,132 @@ impl Animation for WieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - //next.control.ori = Quaternion::rotation_x(slow * 1.0); + next.control.position = Vec3::new(0.0, 0.0, 0.0); + //next.control.orientation = Quaternion::rotation_x(slow * 1.0); // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Axe(_)) => { if velocity < 0.5 { - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, -3.5 + skeleton_attr.head.0, skeleton_attr.head.1 + u_slow * 0.1, ); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(0.35 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.ori = Quaternion::rotation_x(-0.35) + next.chest.orientation = Quaternion::rotation_x(-0.35) * Quaternion::rotation_y(u_slowalt * 0.04) * Quaternion::rotation_z(0.15); - next.belt.offset = + next.belt.position = Vec3::new(0.0, 1.0 + skeleton_attr.belt.0, skeleton_attr.belt.1); - next.belt.ori = Quaternion::rotation_x(0.15) + next.belt.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_y(u_slowalt * 0.03) * Quaternion::rotation_z(0.15); - next.shorts.offset = + next.shorts.position = Vec3::new(0.0, 1.0 + skeleton_attr.shorts.0, skeleton_attr.shorts.1); - next.shorts.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); - next.control.ori = Quaternion::rotation_x(1.8) + next.shorts.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); + next.control.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } else { - next.control.ori = Quaternion::rotation_x(2.1) + next.control.orientation = Quaternion::rotation_x(2.1) * Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(PI - 0.2); next.control.scale = Vec3::one(); } - next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0); - next.l_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0); + next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5); - next.r_hand.ori = Quaternion::rotation_x(PI / 2.0) + next.r_hand.position = Vec3::new(0.5, 0.0, -2.5); + next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(-0.0, -2.0, -1.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(-0.0, -2.0, -1.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-3.0, 11.0, 3.0); + next.control.position = Vec3::new(-3.0, 11.0, 3.0); }, Some(ToolKind::Hammer(_)) => { - next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); + next.l_hand.orientation = + Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.08; - next.r_hand.offset = Vec3::new(2.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.r_hand.position = Vec3::new(2.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.r_hand.scale = Vec3::one() * 1.06; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(-1.57) * Quaternion::rotation_z(1.57); - next.control.offset = Vec3::new(6.0, 7.0, 1.0); - next.control.ori = Quaternion::rotation_x(0.3 + u_slow * 0.15) + next.control.position = Vec3::new(6.0, 7.0, 1.0); + next.control.orientation = Quaternion::rotation_x(0.3 + u_slow * 0.15) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(u_slowalt * 0.08); next.control.scale = Vec3::one(); }, Some(ToolKind::Staff(_)) => { - next.l_hand.offset = Vec3::new(1.5, 0.5, -4.0); - next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); + next.l_hand.position = Vec3::new(1.5, 0.5, -4.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(8.0, 4.0, 2.0); - next.r_hand.ori = Quaternion::rotation_x(1.8) + next.r_hand.position = Vec3::new(8.0, 4.0, 2.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) * Quaternion::rotation_y(0.5) * Quaternion::rotation_z(-0.27); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(12.0, 8.4, 13.2); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(12.0, 8.4, 13.2); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(3.14 + 0.3) * Quaternion::rotation_z(0.9); - next.control.offset = Vec3::new(-14.0, 1.8, 3.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.2) + next.control.position = Vec3::new(-14.0, 1.8, 3.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_y(-0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); @@ -262,116 +270,117 @@ impl Animation for WieldAnimation { // also reduce flicker with overlapping polygons let hand_scale = 1.12; - next.control.offset = Vec3::new(0.0, 0.0, 0.0); - // next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.control.position = Vec3::new(0.0, 0.0, 0.0); + // next.control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.control.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.l_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.l_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.l_hand.scale = Vec3::one() * hand_scale; - next.main.offset = Vec3::new(0.0, 0.0, 0.0); - next.main.ori = Quaternion::rotation_x(0.0 * PI) + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); - next.l_control.offset = Vec3::new(-7.0, 0.0, 0.0); - // next.l_control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0) + next.l_control.position = Vec3::new(-7.0, 0.0, 0.0); + // next.l_control.orientation = Quaternion::rotation_x(u_slow * 0.15 + 1.0) // * Quaternion::rotation_y(0.0) // * Quaternion::rotation_z(u_slowalt * 0.08); // next.l_control.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(0.0, 0.0, 0.0); - next.r_hand.ori = Quaternion::rotation_x(0.0 * PI) + next.r_hand.position = Vec3::new(0.0, 0.0, 0.0); + next.r_hand.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.r_hand.scale = Vec3::one() * hand_scale; - next.second.offset = Vec3::new(0.0, 0.0, 0.0); - next.second.ori = Quaternion::rotation_x(0.0 * PI) + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0 * PI) * Quaternion::rotation_y(0.0 * PI) * Quaternion::rotation_z(0.0 * PI); next.second.scale = Vec3::one(); - next.r_control.offset = Vec3::new(7.0, 0.0, 0.0); - // next.r_control.ori = Quaternion::rotation_x(0.0 * PI) + next.r_control.position = Vec3::new(7.0, 0.0, 0.0); + // next.r_control.orientation = Quaternion::rotation_x(0.0 * PI) // * Quaternion::rotation_y(0.0 * PI) // * Quaternion::rotation_z(0.0 * PI); // next.r_control.scale = Vec3::one(); }, Some(ToolKind::Bow(_)) => { - next.l_hand.offset = Vec3::new(2.0, 1.5, 0.0); - next.l_hand.ori = Quaternion::rotation_x(1.20) + next.l_hand.position = Vec3::new(2.0, 1.5, 0.0); + next.l_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(5.9, 4.5, -5.0); - next.r_hand.ori = Quaternion::rotation_x(1.20) + next.r_hand.position = Vec3::new(5.9, 4.5, -5.0); + next.r_hand.orientation = Quaternion::rotation_x(1.20) * Quaternion::rotation_y(-0.6) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(3.0, 2.0, -13.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(3.0, 2.0, -13.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.3) * Quaternion::rotation_z(-0.6); - next.hold.offset = Vec3::new(1.2, -1.0, -5.2); - next.hold.ori = Quaternion::rotation_x(-1.7) + next.hold.position = Vec3::new(1.2, -1.0, -5.2); + next.hold.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.1); next.hold.scale = Vec3::one() * 1.0; - next.control.offset = Vec3::new(-7.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(-7.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Debug(_)) => { - next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0); - next.l_hand.ori = Quaternion::rotation_x(1.27) + next.l_hand.position = Vec3::new(-7.0, 4.0, 3.0); + next.l_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(7.0, 2.5, -1.25); - next.r_hand.ori = Quaternion::rotation_x(1.27) + next.r_hand.position = Vec3::new(7.0, 2.5, -1.25); + next.r_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(-0.3); next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new(5.0, 8.75, -2.0); - next.main.ori = Quaternion::rotation_x(-0.3) + next.main.position = Vec3::new(5.0, 8.75, -2.0); + next.main.orientation = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.27) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(0.0, 6.0, 6.0); - next.control.ori = + next.control.position = Vec3::new(0.0, 6.0, 6.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.2) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); }, Some(ToolKind::Farming(_)) => { if velocity < 0.5 { - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(-0.2 + head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; } - next.l_hand.offset = Vec3::new(9.0, 1.0, 1.0); - next.l_hand.ori = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); + next.l_hand.position = Vec3::new(9.0, 1.0, 1.0); + next.l_hand.orientation = + Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0); next.l_hand.scale = Vec3::one() * 1.05; - next.r_hand.offset = Vec3::new(9.0, 1.0, 11.0); - next.r_hand.ori = Quaternion::rotation_x(1.57) + next.r_hand.position = Vec3::new(9.0, 1.0, 11.0); + next.r_hand.orientation = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(7.5, 7.5, 13.2); - next.main.ori = Quaternion::rotation_x(0.0) + next.main.position = Vec3::new(7.5, 7.5, 13.2); + next.main.orientation = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(3.14) * Quaternion::rotation_z(0.0); - next.control.offset = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); - next.control.ori = Quaternion::rotation_x(u_slow * 0.1) + next.control.position = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); + next.control.orientation = Quaternion::rotation_x(u_slow * 0.1) * Quaternion::rotation_y(0.6 + u_slow * 0.1) * Quaternion::rotation_z(u_slowalt * 0.1); next.control.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/critter/idle.rs b/voxygen/src/anim/src/critter/idle.rs index 22708a480d..192a89cab0 100644 --- a/voxygen/src/anim/src/critter/idle.rs +++ b/voxygen/src/anim/src/critter/idle.rs @@ -1,7 +1,7 @@ use super::{super::Animation, CritterAttr, CritterSkeleton}; //use std::{f32::consts::PI, ops::Mul}; +use super::super::vek::*; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -37,29 +37,30 @@ impl Animation for IdleAnimation { .sin() * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(rat_head_look.x) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(rat_head_look.x) * Quaternion::rotation_x(rat_head_look.y + wave * 0.03); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 + wave * 0.3, ) / 18.0; - next.chest.ori = Quaternion::rotation_y(wave_slow * 0.06); + next.chest.orientation = Quaternion::rotation_y(wave_slow * 0.06); next.chest.scale = Vec3::one() / 18.0; - next.feet_f.offset = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); - next.feet_f.ori = Quaternion::rotation_z(0.0); + next.feet_f.position = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); + next.feet_f.orientation = Quaternion::rotation_z(0.0); next.feet_f.scale = Vec3::one(); - next.feet_b.offset = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); - next.feet_b.ori = Quaternion::rotation_x(0.0); + next.feet_b.position = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); + next.feet_b.orientation = Quaternion::rotation_x(0.0); next.feet_b.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0 + wave * 0.2, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_y(wave_slow * 0.05); + next.tail.position = + Vec3::new(0.0, skeleton_attr.tail.0 + wave * 0.2, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_y(wave_slow * 0.05); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/critter/jump.rs b/voxygen/src/anim/src/critter/jump.rs index bb82953d97..7630744289 100644 --- a/voxygen/src/anim/src/critter/jump.rs +++ b/voxygen/src/anim/src/critter/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, CritterAttr, CritterSkeleton}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -23,24 +23,24 @@ impl Animation for JumpAnimation { let wave = (_anim_time as f32 * 1.0).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(0.8) * Quaternion::rotation_x(0.5); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(0.8) * Quaternion::rotation_x(0.5); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) / 18.0; - next.chest.ori = Quaternion::rotation_y(0.0); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) / 18.0; + next.chest.orientation = Quaternion::rotation_y(0.0); next.chest.scale = Vec3::one() / 18.0; - next.feet_f.offset = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); - next.feet_f.ori = Quaternion::rotation_x(wave * 0.4); + next.feet_f.position = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); + next.feet_f.orientation = Quaternion::rotation_x(wave * 0.4); next.feet_f.scale = Vec3::one(); - next.feet_b.offset = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); - next.feet_b.ori = Quaternion::rotation_x(wave * 0.4); + next.feet_b.position = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); + next.feet_b.orientation = Quaternion::rotation_x(wave * 0.4); next.feet_b.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_y(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_y(0.0); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/critter/mod.rs b/voxygen/src/anim/src/critter/mod.rs index 2fe638803e..5e50de56cc 100644 --- a/voxygen/src/anim/src/critter/mod.rs +++ b/voxygen/src/anim/src/critter/mod.rs @@ -5,18 +5,18 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; + +skeleton_impls!(struct CritterSkeleton { + + head, + + chest, + + feet_f, + + feet_b, + + tail, +}); -#[derive(Clone, Default)] -pub struct CritterSkeleton { - head: Bone, - chest: Bone, - feet_f: Bone, - feet_b: Bone, - tail: Bone, -} pub struct CritterAttr { head: (f32, f32), chest: (f32, f32), @@ -25,51 +25,30 @@ pub struct CritterAttr { tail: (f32, f32), } -impl CritterSkeleton { - pub fn new() -> Self { Self::default() } -} - impl Skeleton for CritterSkeleton { type Attr = CritterAttr; + const BONE_COUNT: usize = 5; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"critter_compute_mats\0"; - fn bone_count(&self) -> usize { 5 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "critter_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let chest_mat = self.chest.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_mat * self.head.compute_base_matrix()), - FigureBoneData::new(chest_mat), - FigureBoneData::new(chest_mat * self.feet_f.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.feet_b.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.tail.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.chest.interpolate(&target.chest, dt); - self.feet_f.interpolate(&target.feet_f, dt); - self.feet_b.interpolate(&target.feet_b, dt); - self.tail.interpolate(&target.tail, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(chest_mat * Mat4::::from(self.head)), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.feet_f)), + make_bone(chest_mat * Mat4::::from(self.feet_b)), + make_bone(chest_mat * Mat4::::from(self.tail)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/critter/run.rs b/voxygen/src/anim/src/critter/run.rs index cb2019a9a0..2f342e28bd 100644 --- a/voxygen/src/anim/src/critter/run.rs +++ b/voxygen/src/anim/src/critter/run.rs @@ -1,7 +1,7 @@ use super::{super::Animation, CritterAttr, CritterSkeleton}; //use std::{f32::consts::PI, ops::Mul}; +use super::super::vek::*; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -26,30 +26,32 @@ impl Animation for RunAnimation { let wavealt = (anim_time as f32 * 8.0 + PI / 2.0).sin(); let wave_slow = (anim_time as f32 * 6.5 + PI).sin(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + wave * 0.03); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + wave * 0.03); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0 + wave * 1.0, skeleton_attr.chest.1, ) / 18.0; - next.chest.ori = Quaternion::rotation_x(wave * 0.1); + next.chest.orientation = Quaternion::rotation_x(wave * 0.1); next.chest.scale = Vec3::one() / 18.0; - next.feet_f.offset = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); - next.feet_f.ori = + next.feet_f.position = Vec3::new(0.0, skeleton_attr.feet_f.0, skeleton_attr.feet_f.1); + next.feet_f.orientation = Quaternion::rotation_x(wave * 0.8) * Quaternion::rotation_z(wavealt / 6.0); next.feet_f.scale = Vec3::one(); - next.feet_b.offset = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); - next.feet_b.ori = + next.feet_b.position = Vec3::new(0.0, skeleton_attr.feet_b.0, skeleton_attr.feet_b.1); + next.feet_b.orientation = Quaternion::rotation_x(wavealt * 0.8) * Quaternion::rotation_z(wavealt / 6.0); next.feet_b.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0 + wave * 1.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_y(wave_slow * 0.08); + next.tail.position = + Vec3::new(0.0, skeleton_attr.tail.0 + wave * 1.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_y(wave_slow * 0.08); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/dragon/fly.rs b/voxygen/src/anim/src/dragon/fly.rs index 14379e7a42..4602f19cd5 100644 --- a/voxygen/src/anim/src/dragon/fly.rs +++ b/voxygen/src/anim/src/dragon/fly.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, DragonSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + DragonSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct FlyAnimation; @@ -36,123 +38,123 @@ impl Animation for FlyAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + wave_ultra_slow * 0.20, ); - next.head_upper.ori = + next.head_upper.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(wave_ultra_slow * -0.10); next.head_upper.scale = Vec3::one() * 1.05; - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + wave_ultra_slow * 0.20, ); - next.head_lower.ori = + next.head_lower.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(wave_ultra_slow * -0.10); next.head_lower.scale = Vec3::one() * 1.05; - next.jaw.offset = Vec3::new( + next.jaw.position = Vec3::new( 0.0, skeleton_attr.jaw.0 - wave_ultra_slow_cos * 0.12, skeleton_attr.jaw.1 + wave_slow * 0.2, ); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.03); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.03); next.jaw.scale = Vec3::one() * 1.05; - next.tail_front.offset = Vec3::new( + next.tail_front.position = Vec3::new( 0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1 + centeroffset * 0.6, ); - next.tail_front.ori = Quaternion::rotation_x(center * 0.03); + next.tail_front.orientation = Quaternion::rotation_x(center * 0.03); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = Vec3::new( + next.tail_rear.position = Vec3::new( 0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1 + centeroffset * 0.6, ); - next.tail_rear.ori = Quaternion::rotation_x(center * 0.03); + next.tail_rear.orientation = Quaternion::rotation_x(center * 0.03); next.tail_rear.scale = Vec3::one() * 0.98; - next.chest_front.offset = Vec3::new( + next.chest_front.position = Vec3::new( 0.0, skeleton_attr.chest_front.0, skeleton_attr.chest_front.1, ); - next.chest_front.ori = Quaternion::rotation_y(center * 0.05); + next.chest_front.orientation = Quaternion::rotation_y(center * 0.05); next.chest_front.scale = Vec3::one(); - next.chest_rear.offset = + next.chest_rear.position = Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1); - next.chest_rear.ori = Quaternion::rotation_y(center * 0.05); + next.chest_rear.orientation = Quaternion::rotation_y(center * 0.05); next.chest_rear.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_x(-1.3 + footl * 0.06); + next.foot_fl.orientation = Quaternion::rotation_x(-1.3 + footl * 0.06); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_x(-1.3 + footr * 0.06); + next.foot_fr.orientation = Quaternion::rotation_x(-1.3 + footr * 0.06); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_x(-1.3 + footl * 0.06); + next.foot_bl.orientation = Quaternion::rotation_x(-1.3 + footl * 0.06); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_x(-1.3 + footr * 0.06); + next.foot_br.orientation = Quaternion::rotation_x(-1.3 + footr * 0.06); next.foot_br.scale = Vec3::one(); - next.wing_in_l.offset = Vec3::new( + next.wing_in_l.position = Vec3::new( -skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_l.ori = Quaternion::rotation_y(0.4 + wingl * 0.6); + next.wing_in_l.orientation = Quaternion::rotation_y(0.4 + wingl * 0.6); next.wing_in_l.scale = Vec3::one(); - next.wing_in_r.offset = Vec3::new( + next.wing_in_r.position = Vec3::new( skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_r.ori = Quaternion::rotation_y(-0.4 + wingr * 0.6); + next.wing_in_r.orientation = Quaternion::rotation_y(-0.4 + wingr * 0.6); next.wing_in_r.scale = Vec3::one(); - next.wing_out_l.offset = Vec3::new( + next.wing_out_l.position = Vec3::new( -skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_l.ori = Quaternion::rotation_y((0.35 + wingl * 0.6).max(0.2)); + next.wing_out_l.orientation = Quaternion::rotation_y((0.35 + wingl * 0.6).max(0.2)); next.wing_out_l.scale = Vec3::one(); - next.wing_out_r.offset = Vec3::new( + next.wing_out_r.position = Vec3::new( skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_r.ori = Quaternion::rotation_y((-0.35 + wingr * 0.6).min(-0.2)); + next.wing_out_r.orientation = Quaternion::rotation_y((-0.35 + wingr * 0.6).min(-0.2)); next.wing_out_r.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/dragon/idle.rs b/voxygen/src/anim/src/dragon/idle.rs index b61f87fbc4..bee9419896 100644 --- a/voxygen/src/anim/src/dragon/idle.rs +++ b/voxygen/src/anim/src/dragon/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, DragonSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + DragonSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,113 +40,115 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + ultra_slow * 0.20, ); - next.head_upper.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_upper.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(0.8 * dragon_look.y); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + ultra_slow * 0.20, ); - next.head_lower.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_lower.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(-0.2 + 0.8 * dragon_look.y); next.head_lower.scale = Vec3::one() * 1.05; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(slow * 0.04); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(slow * 0.04); next.jaw.scale = Vec3::one() * 1.05; - next.chest_front.offset = Vec3::new( + next.chest_front.position = Vec3::new( 0.0, skeleton_attr.chest_front.0, skeleton_attr.chest_front.1, ); - next.chest_front.ori = Quaternion::rotation_y(0.0); + next.chest_front.orientation = Quaternion::rotation_y(0.0); next.chest_front.scale = Vec3::one() * 1.05; - next.chest_rear.offset = + next.chest_rear.position = Vec3::new(0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1); - next.chest_rear.ori = Quaternion::rotation_y(0.0); + next.chest_rear.orientation = Quaternion::rotation_y(0.0); next.chest_rear.scale = Vec3::one() * 1.05; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_z(slowalt * 0.10) * Quaternion::rotation_x(0.1); + next.tail_front.orientation = + Quaternion::rotation_z(slowalt * 0.10) * Quaternion::rotation_x(0.1); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(0.05); + next.tail_rear.orientation = + Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(0.05); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_x(0.0); + next.foot_fl.orientation = Quaternion::rotation_x(0.0); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_x(0.0); + next.foot_fr.orientation = Quaternion::rotation_x(0.0); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_x(0.0); + next.foot_bl.orientation = Quaternion::rotation_x(0.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_x(0.0); + next.foot_br.orientation = Quaternion::rotation_x(0.0); next.foot_br.scale = Vec3::one(); - next.wing_in_l.offset = Vec3::new( + next.wing_in_l.position = Vec3::new( -skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_l.ori = Quaternion::rotation_y(0.8 + slow * 0.02); + next.wing_in_l.orientation = Quaternion::rotation_y(0.8 + slow * 0.02); next.wing_in_l.scale = Vec3::one(); - next.wing_in_r.offset = Vec3::new( + next.wing_in_r.position = Vec3::new( skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_r.ori = Quaternion::rotation_y(-0.8 - slow * 0.02); + next.wing_in_r.orientation = Quaternion::rotation_y(-0.8 - slow * 0.02); next.wing_in_r.scale = Vec3::one(); - next.wing_out_l.offset = Vec3::new( + next.wing_out_l.position = Vec3::new( -skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_l.ori = Quaternion::rotation_y(-2.0 + slow * 0.02); + next.wing_out_l.orientation = Quaternion::rotation_y(-2.0 + slow * 0.02); next.wing_out_l.scale = Vec3::one(); - next.wing_out_r.offset = Vec3::new( + next.wing_out_r.position = Vec3::new( skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_r.ori = Quaternion::rotation_y(2.0 - slow * 0.02); + next.wing_out_r.orientation = Quaternion::rotation_y(2.0 - slow * 0.02); next.wing_out_r.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/dragon/mod.rs b/voxygen/src/anim/src/dragon/mod.rs index 5a4ec4bbf5..4792a7afdb 100644 --- a/voxygen/src/anim/src/dragon/mod.rs +++ b/voxygen/src/anim/src/dragon/mod.rs @@ -5,112 +5,68 @@ pub mod run; // Reexports pub use self::{fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct DragonSkeleton { - head_upper: Bone, - head_lower: Bone, - jaw: Bone, - chest_front: Bone, - chest_rear: Bone, - tail_front: Bone, - tail_rear: Bone, - wing_in_l: Bone, - wing_in_r: Bone, - wing_out_l: Bone, - wing_out_r: Bone, - foot_fl: Bone, - foot_fr: Bone, - foot_bl: Bone, - foot_br: Bone, -} - -impl DragonSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct DragonSkeleton { + + head_upper, + + head_lower, + + jaw, + + chest_front, + + chest_rear, + + tail_front, + + tail_rear, + + wing_in_l, + + wing_in_r, + + wing_out_l, + + wing_out_r, + + foot_fl, + + foot_fr, + + foot_bl, + + foot_br, +}); impl Skeleton for DragonSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 15; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"dragon_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "dragon_compute_mats")] - fn compute_matrices_inner(&self) -> ([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(); - let chest_rear_mat = self.chest_rear.compute_base_matrix(); - let wing_in_l_mat = self.wing_in_l.compute_base_matrix(); - let wing_in_r_mat = self.wing_in_r.compute_base_matrix(); - 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( - 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( - 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(chest_front_mat * self.foot_fl.compute_base_matrix()), - FigureBoneData::new(chest_front_mat * self.foot_fr.compute_base_matrix()), - FigureBoneData::new( - chest_front_mat - * self.chest_rear.compute_base_matrix() - * self.foot_bl.compute_base_matrix(), - ), - FigureBoneData::new( - chest_front_mat - * self.chest_rear.compute_base_matrix() - * self.foot_br.compute_base_matrix(), - ), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_front_mat = base_mat * Mat4::::from(self.chest_front); + let chest_rear_mat = chest_front_mat * Mat4::::from(self.chest_rear); + let head_lower_mat = chest_front_mat * Mat4::::from(self.head_lower); + let wing_in_l_mat = chest_front_mat * Mat4::::from(self.wing_in_l); + let wing_in_r_mat = chest_front_mat * Mat4::::from(self.wing_in_r); + let tail_front_mat = chest_rear_mat * Mat4::::from(self.tail_front); + let head_upper_mat = head_lower_mat * Mat4::::from(self.head_upper); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head_upper.interpolate(&target.head_upper, dt); - self.head_lower.interpolate(&target.head_lower, dt); - self.jaw.interpolate(&target.jaw, dt); - self.chest_front.interpolate(&target.chest_front, dt); - self.chest_rear.interpolate(&target.chest_rear, dt); - self.tail_front.interpolate(&target.tail_front, dt); - self.tail_rear.interpolate(&target.tail_rear, dt); - self.wing_in_l.interpolate(&target.wing_in_l, dt); - self.wing_in_r.interpolate(&target.wing_in_r, dt); - self.wing_out_l.interpolate(&target.wing_out_l, dt); - self.wing_out_r.interpolate(&target.wing_out_r, dt); - self.foot_fl.interpolate(&target.foot_fl, dt); - self.foot_fr.interpolate(&target.foot_fr, dt); - self.foot_bl.interpolate(&target.foot_bl, dt); - self.foot_br.interpolate(&target.foot_br, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_upper_mat), + make_bone(head_lower_mat), + make_bone(head_upper_mat * Mat4::::from(self.jaw)), + make_bone(chest_front_mat), + make_bone(chest_rear_mat), + make_bone(tail_front_mat), + make_bone(tail_front_mat * Mat4::::from(self.tail_rear)), + make_bone(wing_in_l_mat), + make_bone(wing_in_r_mat), + make_bone(wing_in_l_mat * Mat4::::from(self.wing_out_l)), + make_bone(wing_in_r_mat * Mat4::::from(self.wing_out_r)), + make_bone(chest_front_mat * Mat4::::from(self.foot_fl)), + make_bone(chest_front_mat * Mat4::::from(self.foot_fr)), + make_bone(chest_rear_mat * Mat4::::from(self.foot_bl)), + make_bone(chest_rear_mat * Mat4::::from(self.foot_br)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/dragon/run.rs b/voxygen/src/anim/src/dragon/run.rs index 49f42dcf12..f2ebd5671d 100644 --- a/voxygen/src/anim/src/dragon/run.rs +++ b/voxygen/src/anim/src/dragon/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, DragonSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + DragonSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -37,7 +39,7 @@ impl Animation for RunAnimation { // let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -68,128 +70,128 @@ impl Animation for RunAnimation { let center = (anim_time as f32 * lab as f32 + PI / 2.0).sin(); let centeroffset = (anim_time as f32 * lab as f32 + PI * 1.5).sin(); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(short * -0.03 - 0.1) + next.head_upper.orientation = Quaternion::rotation_x(short * -0.03 - 0.1) * Quaternion::rotation_z(tilt * -1.2) * Quaternion::rotation_y(tilt * 0.8); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(tilt * -0.8) + next.head_lower.orientation = Quaternion::rotation_z(tilt * -0.8) * Quaternion::rotation_x(short * -0.05) * Quaternion::rotation_y(tilt * 0.3); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new( + next.jaw.position = Vec3::new( 0.0, skeleton_attr.jaw.0 - wave_ultra_slow_cos * 0.12, skeleton_attr.jaw.1 + wave_slow * 0.2, ); - next.jaw.ori = Quaternion::rotation_x(wave_slow * 0.03); + next.jaw.orientation = Quaternion::rotation_x(wave_slow * 0.03); next.jaw.scale = Vec3::one() * 1.05; - next.tail_front.offset = Vec3::new( + next.tail_front.position = Vec3::new( 0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1 + centeroffset * 0.6, ); - next.tail_front.ori = + next.tail_front.orientation = Quaternion::rotation_x(center * 0.03) * Quaternion::rotation_z(tilt * 1.5); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = Vec3::new( + next.tail_rear.position = Vec3::new( 0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1 + centeroffset * 0.6, ); - next.tail_rear.ori = + next.tail_rear.orientation = Quaternion::rotation_x(center * 0.03) * Quaternion::rotation_z(tilt * 1.5); next.tail_rear.scale = Vec3::one() * 0.98; - next.chest_front.offset = Vec3::new( + next.chest_front.position = Vec3::new( 0.0, skeleton_attr.chest_front.0, skeleton_attr.chest_front.1 + shortalt * 2.5 + x_tilt * 10.0, ); - next.chest_front.ori = Quaternion::rotation_x(short * 0.13 + x_tilt) + next.chest_front.orientation = Quaternion::rotation_x(short * 0.13 + x_tilt) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(tilt * -1.5); next.chest_front.scale = Vec3::one(); - next.chest_rear.offset = Vec3::new( + next.chest_rear.position = Vec3::new( 0.0, skeleton_attr.chest_rear.0, skeleton_attr.chest_rear.1 + shortalt * 0.2, ); - next.chest_rear.ori = Quaternion::rotation_x(short * 0.1) + next.chest_rear.orientation = Quaternion::rotation_x(short * 0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(tilt * 1.8); next.chest_rear.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + horilf * 2.5, skeleton_attr.feet_f.2 + vertlf * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_fl.ori = Quaternion::rotation_x(horilf * 0.6); + next.foot_fl.orientation = Quaternion::rotation_x(horilf * 0.6); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + horirfoffset * 2.5, skeleton_attr.feet_f.2 + vertrfoffset * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_fr.ori = Quaternion::rotation_x(horirb * 0.6); + next.foot_fr.orientation = Quaternion::rotation_x(horirb * 0.6); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + horilboffset * 3.0, skeleton_attr.feet_b.2 + vertlboffset * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_bl.ori = Quaternion::rotation_x(horilf * 0.55); + next.foot_bl.orientation = Quaternion::rotation_x(horilf * 0.55); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + horirb * 3.0, skeleton_attr.feet_b.2 + vertrb * 5.0 * skeleton_attr.height - 0.5, ); - next.foot_br.ori = Quaternion::rotation_x(horirb * 0.55); + next.foot_br.orientation = Quaternion::rotation_x(horirb * 0.55); next.foot_br.scale = Vec3::one(); - next.wing_in_l.offset = Vec3::new( + next.wing_in_l.position = Vec3::new( -skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_l.ori = Quaternion::rotation_y(0.8 + tilt * 1.0); + next.wing_in_l.orientation = Quaternion::rotation_y(0.8 + tilt * 1.0); next.wing_in_l.scale = Vec3::one(); - next.wing_in_r.offset = Vec3::new( + next.wing_in_r.position = Vec3::new( skeleton_attr.wing_in.0, skeleton_attr.wing_in.1, skeleton_attr.wing_in.2, ); - next.wing_in_r.ori = Quaternion::rotation_y(-0.8 + tilt * 1.0); + next.wing_in_r.orientation = Quaternion::rotation_y(-0.8 + tilt * 1.0); next.wing_in_r.scale = Vec3::one(); - next.wing_out_l.offset = Vec3::new( + next.wing_out_l.position = Vec3::new( -skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_l.ori = Quaternion::rotation_y(-2.0 + tilt * 1.0); + next.wing_out_l.orientation = Quaternion::rotation_y(-2.0 + tilt * 1.0); next.wing_out_l.scale = Vec3::one(); - next.wing_out_r.offset = Vec3::new( + next.wing_out_r.position = Vec3::new( skeleton_attr.wing_out.0, skeleton_attr.wing_out.1, skeleton_attr.wing_out.2, ); - next.wing_out_r.ori = Quaternion::rotation_y(2.0 + tilt * 1.0); + next.wing_out_r.orientation = Quaternion::rotation_y(2.0 + tilt * 1.0); next.wing_out_r.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/fish_medium/idle.rs b/voxygen/src/anim/src/fish_medium/idle.rs index a61c284abd..150a67826d 100644 --- a/voxygen/src/anim/src/fish_medium/idle.rs +++ b/voxygen/src/anim/src/fish_medium/idle.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct IdleAnimation; @@ -21,28 +21,28 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 4.5, 2.0); - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 4.5, 2.0); + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() * 1.01; - next.rear.offset = Vec3::new(0.0, 3.1, -4.5); - next.rear.ori = Quaternion::rotation_z(0.0); + next.rear.position = Vec3::new(0.0, 3.1, -4.5); + next.rear.orientation = Quaternion::rotation_z(0.0); next.rear.scale = Vec3::one() * 0.98; - next.tail.offset = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() / 11.0; - next.fin_l.offset = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.ori = Quaternion::rotation_y(0.0); + next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.orientation = Quaternion::rotation_y(0.0); next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.offset = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.ori = Quaternion::rotation_y(0.0); + next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.orientation = Quaternion::rotation_y(0.0); next.fin_r.scale = Vec3::one() / 10.5; next } diff --git a/voxygen/src/anim/src/fish_medium/jump.rs b/voxygen/src/anim/src/fish_medium/jump.rs index 2858c2ba23..33e679b0e2 100644 --- a/voxygen/src/anim/src/fish_medium/jump.rs +++ b/voxygen/src/anim/src/fish_medium/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -21,28 +21,28 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 4.5, 2.0); - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 4.5, 2.0); + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() * 1.01; - next.rear.offset = Vec3::new(0.0, 3.1, -4.5); - next.rear.ori = Quaternion::rotation_z(0.0); + next.rear.position = Vec3::new(0.0, 3.1, -4.5); + next.rear.orientation = Quaternion::rotation_z(0.0); next.rear.scale = Vec3::one() * 0.98; - next.tail.offset = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() / 11.0; - next.fin_l.offset = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.ori = Quaternion::rotation_y(0.0); + next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.orientation = Quaternion::rotation_y(0.0); next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.offset = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.ori = Quaternion::rotation_y(0.0); + next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.orientation = Quaternion::rotation_y(0.0); next.fin_r.scale = Vec3::one() / 10.5; next } diff --git a/voxygen/src/anim/src/fish_medium/mod.rs b/voxygen/src/anim/src/fish_medium/mod.rs index 7e3741d4c5..97e663bb97 100644 --- a/voxygen/src/anim/src/fish_medium/mod.rs +++ b/voxygen/src/anim/src/fish_medium/mod.rs @@ -5,79 +5,47 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone)] -pub struct FishMediumSkeleton { - head: Bone, - torso: Bone, - rear: Bone, - tail: Bone, - fin_l: Bone, - fin_r: Bone, -} - -impl FishMediumSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self { - head: Bone::default(), - torso: Bone::default(), - rear: Bone::default(), - tail: Bone::default(), - fin_l: Bone::default(), - fin_r: Bone::default(), - } - } -} +skeleton_impls!(struct FishMediumSkeleton { + + head, + + torso, + + rear, + + tail, + + fin_l, + + fin_r, +}); impl Skeleton for FishMediumSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 6; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fish_medium_compute_mats\0"; - fn bone_count(&self) -> usize { 6 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_medium_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); - let rear_mat = self.rear.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); + let rear_mat = torso_mat * Mat4::::from(self.rear); - ( - [ - 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), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.torso.interpolate(&target.torso, dt); - self.rear.interpolate(&target.rear, dt); - self.tail.interpolate(&target.tail, dt); - self.fin_l.interpolate(&target.fin_l, dt); - self.fin_r.interpolate(&target.fin_r, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat * Mat4::::from(self.head)), + make_bone(torso_mat), + make_bone(rear_mat), + make_bone(rear_mat * Mat4::::from(self.tail)), + make_bone(rear_mat * Mat4::::from(self.fin_l)), + make_bone(rear_mat * Mat4::::from(self.fin_r)), + ]; + Vec3::default() } } + pub struct SkeletonAttr; impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr { diff --git a/voxygen/src/anim/src/fish_medium/run.rs b/voxygen/src/anim/src/fish_medium/run.rs index 47219f1b31..4d0823abd3 100644 --- a/voxygen/src/anim/src/fish_medium/run.rs +++ b/voxygen/src/anim/src/fish_medium/run.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishMediumSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct RunAnimation; @@ -21,28 +21,28 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() / 10.88; - next.torso.offset = Vec3::new(0.0, 4.5, 2.0); - next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 4.5, 2.0); + next.torso.orientation = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() * 1.01; - next.rear.offset = Vec3::new(0.0, 3.1, -4.5); - next.rear.ori = Quaternion::rotation_z(0.0); + next.rear.position = Vec3::new(0.0, 3.1, -4.5); + next.rear.orientation = Quaternion::rotation_z(0.0); next.rear.scale = Vec3::one() * 0.98; - next.tail.offset = Vec3::new(0.0, -13.0, 8.0) / 11.0; - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, -13.0, 8.0) / 11.0; + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() / 11.0; - next.fin_l.offset = Vec3::new(0.0, -11.7, 11.0) / 11.0; - next.fin_l.ori = Quaternion::rotation_y(0.0); + next.fin_l.position = Vec3::new(0.0, -11.7, 11.0) / 11.0; + next.fin_l.orientation = Quaternion::rotation_y(0.0); next.fin_l.scale = Vec3::one() / 11.0; - next.fin_r.offset = Vec3::new(0.0, 0.0, 12.0) / 11.0; - next.fin_r.ori = Quaternion::rotation_y(0.0); + next.fin_r.position = Vec3::new(0.0, 0.0, 12.0) / 11.0; + next.fin_r.orientation = Quaternion::rotation_y(0.0); next.fin_r.scale = Vec3::one() / 10.5; next } diff --git a/voxygen/src/anim/src/fish_small/idle.rs b/voxygen/src/anim/src/fish_small/idle.rs index 4262a42db9..841627ec41 100644 --- a/voxygen/src/anim/src/fish_small/idle.rs +++ b/voxygen/src/anim/src/fish_small/idle.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct IdleAnimation; @@ -21,12 +21,12 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.tail.offset = Vec3::new(0.0, 4.5, 2.0); - next.tail.ori = Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, 4.5, 2.0); + next.tail.orientation = Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() * 1.01; next diff --git a/voxygen/src/anim/src/fish_small/jump.rs b/voxygen/src/anim/src/fish_small/jump.rs index 90bca46ade..5af66c2869 100644 --- a/voxygen/src/anim/src/fish_small/jump.rs +++ b/voxygen/src/anim/src/fish_small/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishSmallSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -21,12 +21,12 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.tail.offset = Vec3::new(0.0, 4.5, 2.0); - next.tail.ori = Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, 4.5, 2.0); + next.tail.orientation = Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() * 1.01; next diff --git a/voxygen/src/anim/src/fish_small/mod.rs b/voxygen/src/anim/src/fish_small/mod.rs index 7821d8b446..d3281ff459 100644 --- a/voxygen/src/anim/src/fish_small/mod.rs +++ b/voxygen/src/anim/src/fish_small/mod.rs @@ -5,65 +5,36 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone)] -pub struct FishSmallSkeleton { - torso: Bone, - tail: Bone, -} - -impl FishSmallSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self { - torso: Bone::default(), - tail: Bone::default(), - } - } -} +skeleton_impls!(struct FishSmallSkeleton { + + torso, + + tail, +}); impl Skeleton for FishSmallSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 2; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fish_small_compute_mats\0"; - fn bone_count(&self) -> usize { 2 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "fish_small_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let torso_mat = self.torso.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); - ( - [ - FigureBoneData::new(torso_mat), - FigureBoneData::new(self.tail.compute_base_matrix() * torso_mat), - 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(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.torso.interpolate(&target.torso, dt); - self.tail.interpolate(&target.tail, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(torso_mat), + make_bone(torso_mat * Mat4::::from(self.tail)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/fish_small/run.rs b/voxygen/src/anim/src/fish_small/run.rs index 8e93a3ae7c..f19d685e9e 100644 --- a/voxygen/src/anim/src/fish_small/run.rs +++ b/voxygen/src/anim/src/fish_small/run.rs @@ -1,6 +1,6 @@ use super::{super::Animation, FishSmallSkeleton, SkeletonAttr}; //use std::{f32::consts::PI, ops::Mul}; -use vek::*; +use super::super::vek::*; pub struct RunAnimation; @@ -21,12 +21,12 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.torso.offset = Vec3::new(0.0, 7.5, 15.0) / 11.0; - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 7.5, 15.0) / 11.0; + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 10.88; - next.tail.offset = Vec3::new(0.0, 4.5, 2.0); - next.tail.ori = Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, 4.5, 2.0); + next.tail.orientation = Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one() * 1.01; next diff --git a/voxygen/src/anim/src/fixture/mod.rs b/voxygen/src/anim/src/fixture/mod.rs index 40fe3095fc..633b2a5ec6 100644 --- a/voxygen/src/anim/src/fixture/mod.rs +++ b/voxygen/src/anim/src/fixture/mod.rs @@ -1,49 +1,35 @@ -use super::{FigureBoneData, Skeleton}; -use vek::Vec3; +use super::{make_bone, vek::*, FigureBoneData, Skeleton}; -#[derive(Clone)] +#[derive(Clone, Default)] pub struct FixtureSkeleton; pub struct SkeletonAttr; -impl FixtureSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { Self {} } +impl<'a, Factor> Lerp for &'a FixtureSkeleton { + type Output = FixtureSkeleton; + + fn lerp_unclamped_precise(_from: Self, _to: Self, _factor: Factor) -> Self::Output { + FixtureSkeleton + } + + fn lerp_unclamped(_from: Self, _to: Self, _factor: Factor) -> Self::Output { FixtureSkeleton } } impl Skeleton for FixtureSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 1; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"fixture_compute_mats\0"; - fn bone_count(&self) -> usize { 1 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "fixture_compute_mats")] - fn compute_matrices_inner(&self) -> ([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()), - ], - Vec3::default(), - ) + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + buf[0] = make_bone(base_mat); + Vec3::default() } - - fn interpolate(&mut self, _target: &Self, _dt: f32) {} } diff --git a/voxygen/src/anim/src/golem/idle.rs b/voxygen/src/anim/src/golem/idle.rs index cbdfe7b05e..da453d4629 100644 --- a/voxygen/src/anim/src/golem/idle.rs +++ b/voxygen/src/anim/src/golem/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, GolemSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + GolemSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,88 +40,89 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + torso * 0.2, ) * 1.02; - next.head.ori = Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); + next.head.orientation = + Quaternion::rotation_z(look.x * 0.6) * Quaternion::rotation_x(look.y * 0.6); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + torso * 0.5, ) / 8.0; - next.upper_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one() / 8.0; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2 + torso * 0.6, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0); - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0); + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/golem/jump.rs b/voxygen/src/anim/src/golem/jump.rs index ac294f59dc..277f345ebd 100644 --- a/voxygen/src/anim/src/golem/jump.rs +++ b/voxygen/src/anim/src/golem/jump.rs @@ -1,6 +1,6 @@ use super::{super::Animation, GolemSkeleton, SkeletonAttr}; //use std::f32::consts::PI; -use vek::*; +use super::super::vek::*; pub struct JumpAnimation; @@ -22,84 +22,84 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1, ) / 8.0; - next.upper_torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one() / 8.0; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_l.scale = Vec3::one() / 8.0; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1, skeleton_attr.foot.2, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.foot_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.foot_r.scale = Vec3::one() / 8.0; - next.torso.offset = Vec3::new(0.0, 0.0, 0.0); - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.torso.position = Vec3::new(0.0, 0.0, 0.0); + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/golem/mod.rs b/voxygen/src/anim/src/golem/mod.rs index 5bd5861d1b..015c3d8750 100644 --- a/voxygen/src/anim/src/golem/mod.rs +++ b/voxygen/src/anim/src/golem/mod.rs @@ -5,86 +5,55 @@ pub mod run; // Reexports pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct GolemSkeleton { - head: Bone, - upper_torso: Bone, - shoulder_l: Bone, - shoulder_r: Bone, - hand_l: Bone, - hand_r: Bone, - leg_l: Bone, - leg_r: Bone, - foot_l: Bone, - foot_r: Bone, - torso: Bone, -} - -impl GolemSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct GolemSkeleton { + + head, + + upper_torso, + + shoulder_l, + + shoulder_r, + + hand_l, + + hand_r, + + leg_l, + + leg_r, + + foot_l, + + foot_r, + torso, +}); impl Skeleton for GolemSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 10; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"golem_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "golem_compute_mats")] - fn compute_matrices_inner(&self) -> ([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(); - let leg_l_mat = self.leg_l.compute_base_matrix(); - let leg_r_mat = self.leg_r.compute_base_matrix(); - let torso_mat = self.torso.compute_base_matrix(); - let foot_l_mat = self.foot_l.compute_base_matrix(); - 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), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_mat = base_mat * Mat4::::from(self.torso); + let foot_l_mat = base_mat * Mat4::::from(self.foot_l); + let foot_r_mat = base_mat * Mat4::::from(self.foot_r); + let upper_torso_mat = torso_mat * Mat4::::from(self.upper_torso); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.upper_torso.interpolate(&target.upper_torso, dt); - self.shoulder_l.interpolate(&target.shoulder_l, dt); - self.shoulder_r.interpolate(&target.shoulder_r, dt); - self.hand_l.interpolate(&target.hand_l, dt); - self.hand_r.interpolate(&target.hand_r, dt); - self.leg_l.interpolate(&target.leg_l, dt); - self.leg_r.interpolate(&target.leg_r, dt); - self.foot_l.interpolate(&target.foot_l, dt); - self.foot_r.interpolate(&target.foot_r, dt); - self.torso.interpolate(&target.torso, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(upper_torso_mat * Mat4::::from(self.head)), + make_bone(upper_torso_mat), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_l)), + make_bone(upper_torso_mat * Mat4::::from(self.shoulder_r)), + make_bone(upper_torso_mat * Mat4::::from(self.hand_l)), + make_bone(upper_torso_mat * Mat4::::from(self.hand_r)), + make_bone(foot_l_mat * Mat4::::from(self.leg_l)), + make_bone(foot_r_mat * Mat4::::from(self.leg_r)), + make_bone(foot_l_mat), + make_bone(foot_r_mat), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/golem/run.rs b/voxygen/src/anim/src/golem/run.rs index c5d1c0f6ec..63761bb1a2 100644 --- a/voxygen/src/anim/src/golem/run.rs +++ b/voxygen/src/anim/src/golem/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, GolemSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + GolemSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -42,89 +44,91 @@ impl Animation for RunAnimation { .sqrt()) * ((anim_time as f32 * lab as f32 + PI * 0.4).sin()); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; - next.head.ori = Quaternion::rotation_z(belt * -0.3) * Quaternion::rotation_x(0.3); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) * 1.02; + next.head.orientation = Quaternion::rotation_z(belt * -0.3) * Quaternion::rotation_x(0.3); next.head.scale = Vec3::one() * 1.02; - next.upper_torso.offset = Vec3::new( + next.upper_torso.position = Vec3::new( 0.0, skeleton_attr.upper_torso.0, skeleton_attr.upper_torso.1 + belt * 1.0, ) / 8.0; - next.upper_torso.ori = Quaternion::rotation_z(belt * 0.40) * Quaternion::rotation_x(0.0); + next.upper_torso.orientation = + Quaternion::rotation_z(belt * 0.40) * Quaternion::rotation_x(0.0); next.upper_torso.scale = Vec3::one() / 8.0; - next.shoulder_l.offset = Vec3::new( + next.shoulder_l.position = Vec3::new( -skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_l.ori = + next.shoulder_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotl * -0.15); next.shoulder_l.scale = Vec3::one(); - next.shoulder_r.offset = Vec3::new( + next.shoulder_r.position = Vec3::new( skeleton_attr.shoulder.0, skeleton_attr.shoulder.1, skeleton_attr.shoulder.2, ); - next.shoulder_r.ori = + next.shoulder_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotr * -0.15); next.shoulder_r.scale = Vec3::one(); - next.hand_l.offset = Vec3::new( + next.hand_l.position = Vec3::new( -skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_l.ori = + next.hand_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.3 + footrotl * -0.8); next.hand_l.scale = Vec3::one() * 1.02; - next.hand_r.offset = Vec3::new( + next.hand_r.position = Vec3::new( skeleton_attr.hand.0, skeleton_attr.hand.1, skeleton_attr.hand.2, ); - next.hand_r.ori = + next.hand_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.3 + footrotr * -0.8); next.hand_r.scale = Vec3::one() * 1.02; - next.leg_l.offset = Vec3::new( + next.leg_l.position = Vec3::new( -skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_l.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_l.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_l.scale = Vec3::one() * 1.02; - next.leg_r.offset = Vec3::new( + next.leg_r.position = Vec3::new( skeleton_attr.leg.0, skeleton_attr.leg.1, skeleton_attr.leg.2, ) * 1.02; - next.leg_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.leg_r.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.leg_r.scale = Vec3::one() * 1.02; - next.foot_l.offset = Vec3::new( + next.foot_l.position = Vec3::new( -skeleton_attr.foot.0, skeleton_attr.foot.1 + foothoril * 8.0 + 3.0, skeleton_attr.foot.2 + footvertl * 4.0, ) / 8.0; - next.foot_l.ori = Quaternion::rotation_x(footrotl * 0.7); + next.foot_l.orientation = Quaternion::rotation_x(footrotl * 0.7); next.foot_l.scale = Vec3::one() / 8.0 * 0.98; - next.foot_r.offset = Vec3::new( + next.foot_r.position = Vec3::new( skeleton_attr.foot.0, skeleton_attr.foot.1 + foothorir * 8.0 + 3.0, skeleton_attr.foot.2 + footvertr * 4.0, ) / 8.0; - next.foot_r.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotr * 0.7); + next.foot_r.orientation = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(footrotr * 0.7); next.foot_r.scale = Vec3::one() / 8.0 * 0.98; - next.torso.offset = Vec3::new(0.0, 0.0, belt * 0.15); - next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.2); + next.torso.position = Vec3::new(0.0, 0.0, belt * 0.15); + next.torso.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/lib.rs b/voxygen/src/anim/src/lib.rs index 8fa5542505..e12290e9e9 100644 --- a/voxygen/src/anim/src/lib.rs +++ b/voxygen/src/anim/src/lib.rs @@ -1,7 +1,44 @@ +#![feature(const_generics)] #![feature(or_patterns)] +#![allow(incomplete_features)] #[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); +macro_rules! skeleton_impls { + { struct $Skeleton:ident { $( $(+)? $bone:ident ),* $(,)? } } => { + #[derive(Clone, Default)] + pub struct $Skeleton { + $( + $bone: Bone, + )* + } + + impl<'a, Factor> Lerp for &'a $Skeleton + where + Factor: Copy, + Bone: Lerp + { + type Output = $Skeleton; + + fn lerp_unclamped_precise(from: Self, to: Self, factor: Factor) -> Self::Output { + Self::Output { + $( + $bone: Lerp::lerp_unclamped_precise(from.$bone, to.$bone, factor), + )* + } + } + + fn lerp_unclamped(from: Self, to: Self, factor: Factor) -> Self::Output { + Self::Output { + $( + $bone: Lerp::lerp_unclamped(from.$bone, to.$bone, factor), + )* + } + } + } + } +} + pub mod biped_large; pub mod bird_medium; pub mod bird_small; @@ -17,94 +54,73 @@ pub mod object; pub mod quadruped_low; pub mod quadruped_medium; pub mod quadruped_small; +pub mod vek; #[cfg(feature = "use-dyn-lib")] pub use dyn_lib::init; #[cfg(feature = "use-dyn-lib")] use std::ffi::CStr; -use vek::*; -// TODO: replace with inner type everywhere -pub struct FigureBoneData(pub Mat4); -impl FigureBoneData { - pub fn new(mat: Mat4) -> Self { Self(mat) } +use self::vek::*; - pub fn default() -> Self { Self(Mat4::identity()) } +type MatRaw = [[f32; 4]; 4]; + +pub type FigureBoneData = (MatRaw, MatRaw); + +pub const MAX_BONE_COUNT: usize = 16; + +fn make_bone(mat: Mat4) -> FigureBoneData { + let normal = mat.map_cols(Vec4::normalized); + (mat.into_col_arrays(), normal.into_col_arrays()) } -#[derive(Copy, Clone, Debug)] -pub struct Bone { - pub offset: Vec3, - pub ori: Quaternion, - pub scale: Vec3, -} - -impl Default for Bone { - fn default() -> Self { - Self { - offset: Vec3::zero(), - ori: Quaternion::identity(), - scale: Vec3::broadcast(1.0 / 11.0), - } - } -} - -impl Bone { - pub fn compute_base_matrix(&self) -> Mat4 { - Mat4::::translation_3d(self.offset) - * Mat4::scaling_3d(self.scale) - * Mat4::from(self.ori) - } - - /// Change the current bone to be more like `target`. - fn interpolate(&mut self, target: &Bone, dt: f32) { - // TODO: Make configurable. - let factor = (15.0 * dt).min(1.0); - self.offset += (target.offset - self.offset) * factor; - self.ori = vek::Slerp::slerp(self.ori, target.ori, factor); - self.scale += (target.scale - self.scale) * factor; - } -} +pub type Bone = Transform; pub trait Skeleton: Send + Sync + 'static { type Attr; + const BONE_COUNT: usize; + #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8]; - fn bone_count(&self) -> usize { 16 } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; MAX_BONE_COUNT], + ) -> Vec3; +} - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3); - - fn compute_matrices(&self) -> ([FigureBoneData; 16], Vec3) { - #[cfg(not(feature = "use-dyn-lib"))] - { - Self::compute_matrices_inner(self) - } - #[cfg(feature = "use-dyn-lib")] - { - let lock = dyn_lib::LIB.lock().unwrap(); - let lib = &lock.as_ref().unwrap().lib; - - let compute_fn: libloading::Symbol ([FigureBoneData; 16], Vec3)> = - unsafe { lib.get(Self::COMPUTE_FN) }.unwrap_or_else(|e| { - panic!( - "Trying to use: {} but had error: {:?}", - CStr::from_bytes_with_nul(Self::COMPUTE_FN) - .map(CStr::to_str) - .unwrap() - .unwrap(), - e - ) - }); - - compute_fn(self) - } +pub fn compute_matrices( + skeleton: &S, + base_mat: Mat4, + buf: &mut [FigureBoneData; MAX_BONE_COUNT], +) -> Vec3 { + #[cfg(not(feature = "use-dyn-lib"))] + { + S::compute_matrices_inner(skeleton, base_mat, buf) } + #[cfg(feature = "use-dyn-lib")] + { + let lock = dyn_lib::LIB.lock().unwrap(); + let lib = &lock.as_ref().unwrap().lib; - /// Change the current skeleton to be more like `target`. - fn interpolate(&mut self, target: &Self, dt: f32); + let compute_fn: libloading::Symbol< + fn(&S, Mat4, &mut [FigureBoneData; MAX_BONE_COUNT]) -> Vec3, + > = unsafe { lib.get(S::COMPUTE_FN) }.unwrap_or_else(|e| { + panic!( + "Trying to use: {} but had error: {:?}", + CStr::from_bytes_with_nul(S::COMPUTE_FN) + .map(CStr::to_str) + .unwrap() + .unwrap(), + e + ) + }); + + compute_fn(skeleton, base_mat, buf) + } } pub trait Animation { diff --git a/voxygen/src/anim/src/object/mod.rs b/voxygen/src/anim/src/object/mod.rs index 398e6d80be..d0613aa59c 100644 --- a/voxygen/src/anim/src/object/mod.rs +++ b/voxygen/src/anim/src/object/mod.rs @@ -1,49 +1,36 @@ -use super::{FigureBoneData, Skeleton}; -use vek::*; +use super::{make_bone, vek::*, FigureBoneData, Skeleton}; -#[derive(Clone)] +#[derive(Clone, Default)] pub struct ObjectSkeleton; -pub struct SkeletonAttr; -impl ObjectSkeleton { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { Self {} } +impl<'a, Factor> Lerp for &'a ObjectSkeleton { + type Output = ObjectSkeleton; + + fn lerp_unclamped_precise(_from: Self, _to: Self, _factor: Factor) -> Self::Output { + ObjectSkeleton + } + + fn lerp_unclamped(_from: Self, _to: Self, _factor: Factor) -> Self::Output { ObjectSkeleton } } +pub struct SkeletonAttr; + const SCALE: f32 = 1.0 / 11.0; impl Skeleton for ObjectSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 1; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"object_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "object_compute_mats")] - fn compute_matrices_inner(&self) -> ([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()), - ], - Vec3::default(), - ) + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + buf[0] = make_bone(base_mat * Mat4::scaling_3d(SCALE)); + Vec3::default() } - - fn interpolate(&mut self, _target: &Self, _dt: f32) {} } diff --git a/voxygen/src/anim/src/quadruped_low/alpha.rs b/voxygen/src/anim/src/quadruped_low/alpha.rs index 0e60916168..87eedd579b 100644 --- a/voxygen/src/anim/src/quadruped_low/alpha.rs +++ b/voxygen/src/anim/src/quadruped_low/alpha.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -29,71 +31,72 @@ impl Animation for AlphaAnimation { / (0.001 + 0.9999 * ((anim_time as f32 * 7.0 + PI * 0.0).sin()).powf(2.0 as f32))) .sqrt()) * ((anim_time as f32 * 7.0 + PI * 0.0).sin()); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_z(short * 0.3) * Quaternion::rotation_x(0.0); + next.head_upper.orientation = + Quaternion::rotation_z(short * 0.3) * Quaternion::rotation_x(0.0); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = + next.head_lower.orientation = Quaternion::rotation_z(short * 0.2) * Quaternion::rotation_y(short * -0.4); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.2 + quick * 0.3); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.2 + quick * 0.3); next.jaw.scale = Vec3::one() * 0.98; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(short * -0.07); + next.chest.orientation = Quaternion::rotation_y(short * -0.07); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_x(0.15) + next.tail_front.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_y(short * 0.2) * Quaternion::rotation_z(short * 0.3); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_y(short * 0.5) + next.tail_rear.orientation = Quaternion::rotation_y(short * 0.5) * Quaternion::rotation_x(-0.12) * Quaternion::rotation_z(short * 0.3); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_y(short * 0.12); + next.foot_fl.orientation = Quaternion::rotation_y(short * 0.12); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_y(short * 0.12); + next.foot_fr.orientation = Quaternion::rotation_y(short * 0.12); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_y(short * 0.12); + next.foot_bl.orientation = Quaternion::rotation_y(short * 0.12); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_y(short * 0.12); + next.foot_br.orientation = Quaternion::rotation_y(short * 0.12); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_low/idle.rs b/voxygen/src/anim/src/quadruped_low/idle.rs index 1a7ef52347..091f129c42 100644 --- a/voxygen/src/anim/src/quadruped_low/idle.rs +++ b/voxygen/src/anim/src/quadruped_low/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,74 +40,76 @@ impl Animation for IdleAnimation { * 0.1, ); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + slower * 0.20, ); - next.head_upper.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_upper.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(0.8 * dragon_look.y); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + slower * 0.20, ); - next.head_lower.ori = Quaternion::rotation_z(0.8 * dragon_look.x) + next.head_lower.orientation = Quaternion::rotation_z(0.8 * dragon_look.x) * Quaternion::rotation_x(0.8 * dragon_look.y); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(slow * 0.04); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(slow * 0.04); next.jaw.scale = Vec3::one() * 0.98; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(slow * 0.03); + next.chest.orientation = Quaternion::rotation_y(slow * 0.03); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(slowalt * 0.12); + next.tail_front.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(slowalt * 0.12); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(-0.12); + next.tail_rear.orientation = + Quaternion::rotation_z(slowalt * 0.12) * Quaternion::rotation_x(-0.12); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_fl.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_fr.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_bl.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_y(slow * -0.05); + next.foot_br.orientation = Quaternion::rotation_y(slow * -0.05); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_low/jump.rs b/voxygen/src/anim/src/quadruped_low/jump.rs index ba851f9a52..6e1266cf8f 100644 --- a/voxygen/src/anim/src/quadruped_low/jump.rs +++ b/voxygen/src/anim/src/quadruped_low/jump.rs @@ -1,5 +1,7 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; -use vek::*; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; pub struct JumpAnimation; @@ -20,66 +22,66 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.0); + next.head_upper.orientation = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.0); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(0.2); + next.head_lower.orientation = Quaternion::rotation_z(0.2); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.3); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.3); next.jaw.scale = Vec3::one() * 0.98; - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(0.0); + next.chest.orientation = Quaternion::rotation_y(0.0); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.tail_front.offset = + next.tail_front.position = Vec3::new(0.0, skeleton_attr.tail_front.0, skeleton_attr.tail_front.1); - next.tail_front.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(-0.2); + next.tail_front.orientation = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(-0.2); next.tail_front.scale = Vec3::one() * 0.98; - next.tail_rear.offset = + next.tail_rear.position = Vec3::new(0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1); - next.tail_rear.ori = Quaternion::rotation_z(-0.4) * Quaternion::rotation_x(-0.12); + next.tail_rear.orientation = Quaternion::rotation_z(-0.4) * Quaternion::rotation_x(-0.12); next.tail_rear.scale = Vec3::one() * 0.98; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_z(0.3); + next.foot_fl.orientation = Quaternion::rotation_z(0.3); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_z(0.3); + next.foot_fr.orientation = Quaternion::rotation_z(0.3); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_y(0.0); + next.foot_bl.orientation = Quaternion::rotation_y(0.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_y(0.0); + next.foot_br.orientation = Quaternion::rotation_y(0.0); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_low/mod.rs b/voxygen/src/anim/src/quadruped_low/mod.rs index cc621e6667..d36eba621b 100644 --- a/voxygen/src/anim/src/quadruped_low/mod.rs +++ b/voxygen/src/anim/src/quadruped_low/mod.rs @@ -8,81 +8,54 @@ pub use self::{ alpha::AlphaAnimation, idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct QuadrupedLowSkeleton { - head_upper: Bone, - head_lower: Bone, - jaw: Bone, - chest: Bone, - tail_front: Bone, - tail_rear: Bone, - foot_fl: Bone, - foot_fr: Bone, - foot_bl: Bone, - foot_br: Bone, -} - -impl QuadrupedLowSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct QuadrupedLowSkeleton { + + head_upper, + + head_lower, + + jaw, + + chest, + + tail_front, + + tail_rear, + + foot_fl, + + foot_fr, + + foot_bl, + + foot_br, +}); impl Skeleton for QuadrupedLowSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 10; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"quadruped_low_compute_mats\0"; - fn bone_count(&self) -> usize { 10 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_low_compute_mats")] - fn compute_matrices_inner(&self) -> ([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_mat = self.chest.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_mat * head_lower_mat * head_upper_mat), - FigureBoneData::new(chest_mat * head_lower_mat), - FigureBoneData::new( - chest_mat * head_lower_mat * head_upper_mat * self.jaw.compute_base_matrix(), - ), - FigureBoneData::new(chest_mat), - FigureBoneData::new(chest_mat * self.tail_front.compute_base_matrix()), - FigureBoneData::new( - chest_mat - * self.tail_front.compute_base_matrix() - * self.tail_rear.compute_base_matrix(), - ), - FigureBoneData::new(chest_mat * self.foot_fl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.foot_fr.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.foot_bl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.foot_br.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); + let tail_front = chest_mat * Mat4::::from(self.tail_front); + let head_lower_mat = chest_mat * Mat4::::from(self.head_lower); + let head_upper_mat = head_lower_mat * Mat4::::from(self.head_upper); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head_upper.interpolate(&target.head_upper, dt); - self.head_lower.interpolate(&target.head_lower, dt); - self.jaw.interpolate(&target.jaw, dt); - self.chest.interpolate(&target.chest, dt); - self.tail_front.interpolate(&target.tail_front, dt); - self.tail_rear.interpolate(&target.tail_rear, dt); - self.foot_fl.interpolate(&target.foot_fl, dt); - self.foot_fr.interpolate(&target.foot_fr, dt); - self.foot_bl.interpolate(&target.foot_bl, dt); - self.foot_br.interpolate(&target.foot_br, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_upper_mat), + make_bone(head_lower_mat), + make_bone(head_upper_mat * Mat4::::from(self.jaw)), + make_bone(chest_mat), + make_bone(tail_front), + make_bone(tail_front * Mat4::::from(self.tail_rear)), + make_bone(chest_mat * Mat4::::from(self.foot_fl)), + make_bone(chest_mat * Mat4::::from(self.foot_fr)), + make_bone(chest_mat * Mat4::::from(self.foot_bl)), + make_bone(chest_mat * Mat4::::from(self.foot_br)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/quadruped_low/run.rs b/voxygen/src/anim/src/quadruped_low/run.rs index a71979ee24..54c876e084 100644 --- a/voxygen/src/anim/src/quadruped_low/run.rs +++ b/voxygen/src/anim/src/quadruped_low/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedLowSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -72,7 +74,7 @@ impl Animation for RunAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -83,94 +85,96 @@ impl Animation for RunAnimation { } else { 0.0 } * 1.3; - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(-skeleton_attr.lean.0 + x_tilt * -1.0) + next.head_upper.orientation = Quaternion::rotation_x(-skeleton_attr.lean.0 + x_tilt * -1.0) * Quaternion::rotation_y(tilt * 0.3) * Quaternion::rotation_z(short * -0.06 + tilt * -1.5); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_y(tilt * 1.0) + next.head_lower.orientation = Quaternion::rotation_y(tilt * 1.0) * Quaternion::rotation_z(short * -0.15 + tilt * -0.8) * Quaternion::rotation_x(x_tilt * 0.4); next.head_lower.scale = Vec3::one(); - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(0.0); next.jaw.scale = Vec3::one() * 0.98; - next.tail_front.offset = Vec3::new( + next.tail_front.position = Vec3::new( 0.0, skeleton_attr.tail_front.0 + skeleton_attr.lean.0 * 2.0, skeleton_attr.tail_front.1 + skeleton_attr.lean.0 * 2.0, ); - next.tail_front.ori = + next.tail_front.orientation = Quaternion::rotation_z(shortalt * 0.18 * skeleton_attr.lean.1 + tilt * 1.8) * Quaternion::rotation_y(shortalt * 0.1) * Quaternion::rotation_x(0.06 - skeleton_attr.lean.0 * 1.2 + x_tilt * 0.2); next.tail_front.scale = Vec3::one(); - next.tail_rear.offset = Vec3::new( + next.tail_rear.position = Vec3::new( 0.0, skeleton_attr.tail_rear.0, skeleton_attr.tail_rear.1 + shortalt * 0.6, ); - next.tail_rear.ori = + next.tail_rear.orientation = Quaternion::rotation_z(shortalt * 0.25 * skeleton_attr.lean.1 + tilt * 1.6) * Quaternion::rotation_y(shortalt * 0.1) * Quaternion::rotation_x(-0.04 + x_tilt * 0.5); next.tail_rear.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_z(short * 0.13 + tilt * -1.9) + next.chest.orientation = Quaternion::rotation_z(short * 0.13 + tilt * -1.9) * Quaternion::rotation_y(short * 0.12 + tilt * 0.7) * Quaternion::rotation_x(x_tilt + skeleton_attr.lean.0); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + foothoril * -2.0, skeleton_attr.feet_f.2 + 1.0 + ((footvertl * -1.8).max(-0.0)), ); - next.foot_fl.ori = Quaternion::rotation_x( + next.foot_fl.orientation = Quaternion::rotation_x( -0.2 + footvertl * -0.45 * skeleton_attr.lean.1 - skeleton_attr.lean.0, ) * Quaternion::rotation_y(tilt * -1.0) * Quaternion::rotation_z(foothoril * 0.4 * skeleton_attr.lean.1 + tilt * -2.0); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + foothorir * -2.0, skeleton_attr.feet_f.2 + 1.0 + ((footvertr * -1.8).max(-0.0)), ); - next.foot_fr.ori = Quaternion::rotation_x( + next.foot_fr.orientation = Quaternion::rotation_x( -0.2 + footvertr * -0.45 * skeleton_attr.lean.1 - skeleton_attr.lean.0, ) * Quaternion::rotation_y(tilt * -1.0) * Quaternion::rotation_z(foothorir * -0.4 * skeleton_attr.lean.1 + tilt * -2.0); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + foothorilb * -1.0, skeleton_attr.feet_b.2 + ((footvertlb * -1.2).max(-0.0)), ); - next.foot_bl.ori = Quaternion::rotation_x(-0.2 + footvertlb * -0.5 - skeleton_attr.lean.0) - * Quaternion::rotation_y(tilt * -1.0) - * Quaternion::rotation_z(foothorilb * 0.4 + tilt * -2.0); + next.foot_bl.orientation = + Quaternion::rotation_x(-0.2 + footvertlb * -0.5 - skeleton_attr.lean.0) + * Quaternion::rotation_y(tilt * -1.0) + * Quaternion::rotation_z(foothorilb * 0.4 + tilt * -2.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + foothorirb * -1.0, skeleton_attr.feet_b.2 + ((footvertrb * -1.2).max(-0.0)), ); - next.foot_br.ori = Quaternion::rotation_x(-0.2 + footvertrb * -0.5 - skeleton_attr.lean.0) - * Quaternion::rotation_y(tilt * -1.0) - * Quaternion::rotation_z(foothorirb * -0.4 + tilt * -2.0); + next.foot_br.orientation = + Quaternion::rotation_x(-0.2 + footvertrb * -0.5 - skeleton_attr.lean.0) + * Quaternion::rotation_y(tilt * -1.0) + * Quaternion::rotation_z(foothorirb * -0.4 + tilt * -2.0); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_medium/alpha.rs b/voxygen/src/anim/src/quadruped_medium/alpha.rs index 80df941140..ae84f5f8ce 100644 --- a/voxygen/src/anim/src/quadruped_medium/alpha.rs +++ b/voxygen/src/anim/src/quadruped_medium/alpha.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct AlphaAnimation; @@ -30,114 +32,114 @@ impl Animation for AlphaAnimation { .sqrt()) * ((anim_time as f32 * 4.0 + PI * 0.5).sin()); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = + next.head_upper.orientation = Quaternion::rotation_y(short * -0.2) * Quaternion::rotation_x(0.1 + short * 0.2); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head_lower.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.3 + quick * 0.4); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.3 + quick * 0.4); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0 + short * 2.8, skeleton_attr.torso_front.1 + short * 1.0, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_y(short * -0.1); + next.torso_front.orientation = Quaternion::rotation_y(short * -0.1); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = + next.torso_back.position = Vec3::new(0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1); - next.torso_back.ori = Quaternion::rotation_y(short * -0.1) + next.torso_back.orientation = Quaternion::rotation_y(short * -0.1) * Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(0.0); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(0.0); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fl.ori = + next.leg_fl.orientation = Quaternion::rotation_x(short * -0.1) * Quaternion::rotation_y(short * 0.15); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fr.ori = + next.leg_fr.orientation = Quaternion::rotation_x(short * 0.3) * Quaternion::rotation_y(short * -0.2); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + 1.0, ); - next.leg_bl.ori = + next.leg_bl.orientation = Quaternion::rotation_x(-0.1 + short * -0.2) * Quaternion::rotation_y(short * 0.2); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + 1.0, ); - next.leg_br.ori = + next.leg_br.orientation = Quaternion::rotation_x(-0.1 + short * -0.2) * Quaternion::rotation_y(0.1 + short * 0.2); next.leg_br.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + short * -0.2, ); - next.foot_fl.ori = Quaternion::rotation_x(short * -0.05); + next.foot_fl.orientation = Quaternion::rotation_x(short * -0.05); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = + next.foot_fr.orientation = Quaternion::rotation_x(short * -0.4) * Quaternion::rotation_y(short * 0.15); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + short * -0.8, ); - next.foot_bl.ori = + next.foot_bl.orientation = Quaternion::rotation_x(-0.2 + short * 0.2) * Quaternion::rotation_y(short * 0.15); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = + next.foot_br.orientation = Quaternion::rotation_x(-0.2 + short * 0.2) * Quaternion::rotation_y(short * 0.15); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_medium/idle.rs b/voxygen/src/anim/src/quadruped_medium/idle.rs index b41ebcc636..193bb3aa6e 100644 --- a/voxygen/src/anim/src/quadruped_medium/idle.rs +++ b/voxygen/src/anim/src/quadruped_medium/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -49,121 +51,121 @@ impl Animation for IdleAnimation { * 0.125, ); - next.head_upper.offset = Vec3::new( + next.head_upper.position = Vec3::new( 0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1 + slower * 0.2, ); - next.head_upper.ori = + next.head_upper.orientation = Quaternion::rotation_z(0.3 * look.x) * Quaternion::rotation_x(0.3 * look.y); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = Vec3::new( + next.head_lower.position = Vec3::new( 0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1 + slower * 0.1, ); - next.head_lower.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); + next.head_lower.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new( + next.jaw.position = Vec3::new( 0.0, skeleton_attr.jaw.0 - slower * 0.12, skeleton_attr.jaw.1 + slow * 0.2, ); - next.jaw.ori = Quaternion::rotation_x(slow * 0.05); + next.jaw.orientation = Quaternion::rotation_x(slow * 0.05); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0 + slow * 0.2 + tailmove.x) * Quaternion::rotation_x(0.0); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1 + slower * 0.3, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_y(slow * 0.02); + next.torso_front.orientation = Quaternion::rotation_y(slow * 0.02); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = Vec3::new( + next.torso_back.position = Vec3::new( 0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1 + slower * 0.2, ); - next.torso_back.ori = Quaternion::rotation_y(-slow * 0.005) + next.torso_back.orientation = Quaternion::rotation_y(-slow * 0.005) * Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso_back.scale = Vec3::one() * 0.99; - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(0.0 + slower * 0.03); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(0.0 + slower * 0.03); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2 + slow * -0.15 + slower * -0.15, ); - next.leg_fl.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_fl.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2 + slow * 0.15 + slower * -0.15, ); - next.leg_fr.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_fr.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + slower * -0.3, ); - next.leg_bl.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_bl.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2 + slower * -0.3, ); - next.leg_br.ori = Quaternion::rotation_y(slow * -0.02); + next.leg_br.orientation = Quaternion::rotation_y(slow * -0.02); next.leg_br.scale = Vec3::one() * 1.02; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slower * -0.2, ); - next.foot_fl.ori = Quaternion::rotation_x(0.0); + next.foot_fl.orientation = Quaternion::rotation_x(0.0); next.foot_fl.scale = Vec3::one() * 0.94; - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slower * -0.2, ); - next.foot_fr.ori = Quaternion::rotation_x(0.0); + next.foot_fr.orientation = Quaternion::rotation_x(0.0); next.foot_fr.scale = Vec3::one() * 0.94; - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slower * -0.2, ); - next.foot_bl.ori = Quaternion::rotation_x(0.0); + next.foot_bl.orientation = Quaternion::rotation_x(0.0); next.foot_bl.scale = Vec3::one() * 0.94; - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slower * -0.2, ); - next.foot_br.ori = Quaternion::rotation_x(0.0); + next.foot_br.orientation = Quaternion::rotation_x(0.0); next.foot_br.scale = Vec3::one() * 0.94; next diff --git a/voxygen/src/anim/src/quadruped_medium/jump.rs b/voxygen/src/anim/src/quadruped_medium/jump.rs index eb8647aecc..5d70d11b3c 100644 --- a/voxygen/src/anim/src/quadruped_medium/jump.rs +++ b/voxygen/src/anim/src/quadruped_medium/jump.rs @@ -1,5 +1,7 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; -use vek::*; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; pub struct JumpAnimation; @@ -20,106 +22,106 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.3); + next.head_upper.orientation = Quaternion::rotation_z(0.4) * Quaternion::rotation_x(0.3); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(0.2) * Quaternion::rotation_x(0.3); + next.head_lower.orientation = Quaternion::rotation_z(0.2) * Quaternion::rotation_x(0.3); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(-0.4); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(-0.4); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.3); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(-0.3); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_y(0.0); + next.torso_front.orientation = Quaternion::rotation_y(0.0); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = + next.torso_back.position = Vec3::new(0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1); - next.torso_back.ori = Quaternion::rotation_z(-0.3) + next.torso_back.orientation = Quaternion::rotation_z(-0.3) * Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(0.6); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(0.6); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fl.ori = Quaternion::rotation_x(-0.4); + next.leg_fl.orientation = Quaternion::rotation_x(-0.4); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1, skeleton_attr.leg_f.2, ); - next.leg_fr.ori = Quaternion::rotation_x(0.4); + next.leg_fr.orientation = Quaternion::rotation_x(0.4); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2, ); - next.leg_bl.ori = Quaternion::rotation_y(0.0); + next.leg_bl.orientation = Quaternion::rotation_y(0.0); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1, skeleton_attr.leg_b.2, ); - next.leg_br.ori = Quaternion::rotation_y(0.0); + next.leg_br.orientation = Quaternion::rotation_y(0.0); next.leg_br.scale = Vec3::one(); - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fl.ori = Quaternion::rotation_x(-0.3); + next.foot_fl.orientation = Quaternion::rotation_x(-0.3); next.foot_fl.scale = Vec3::one(); - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.foot_fr.ori = Quaternion::rotation_x(0.2); + next.foot_fr.orientation = Quaternion::rotation_x(0.2); next.foot_fr.scale = Vec3::one(); - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_bl.ori = Quaternion::rotation_x(0.0); + next.foot_bl.orientation = Quaternion::rotation_x(0.0); next.foot_bl.scale = Vec3::one(); - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.foot_br.ori = Quaternion::rotation_x(0.0); + next.foot_br.orientation = Quaternion::rotation_x(0.0); next.foot_br.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_medium/mod.rs b/voxygen/src/anim/src/quadruped_medium/mod.rs index 4a1fefe2bf..08e5db484b 100644 --- a/voxygen/src/anim/src/quadruped_medium/mod.rs +++ b/voxygen/src/anim/src/quadruped_medium/mod.rs @@ -8,113 +8,68 @@ pub use self::{ alpha::AlphaAnimation, idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, }; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct QuadrupedMediumSkeleton { - head_upper: Bone, - head_lower: Bone, - jaw: Bone, - tail: Bone, - torso_front: Bone, - torso_back: Bone, - ears: Bone, - leg_fl: Bone, - leg_fr: Bone, - leg_bl: Bone, - leg_br: Bone, - foot_fl: Bone, - foot_fr: Bone, - foot_bl: Bone, - foot_br: Bone, -} - -impl QuadrupedMediumSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct QuadrupedMediumSkeleton { + + head_upper, + + head_lower, + + jaw, + + tail, + + torso_front, + + torso_back, + + ears, + + leg_fl, + + leg_fr, + + leg_bl, + + leg_br, + + foot_fl, + + foot_fr, + + foot_bl, + + foot_br, +}); impl Skeleton for QuadrupedMediumSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 15; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"quadruped_medium_compute_mats\0"; - fn bone_count(&self) -> usize { 15 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_medium_compute_mats")] - fn compute_matrices_inner(&self) -> ([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_front_mat = self.torso_front.compute_base_matrix(); - let torso_back_mat = self.torso_back.compute_base_matrix(); - let leg_fl_mat = self.leg_fl.compute_base_matrix(); - let leg_fr_mat = self.leg_fr.compute_base_matrix(); - let leg_bl_mat = self.leg_bl.compute_base_matrix(); - let leg_br_mat = self.leg_br.compute_base_matrix(); + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let torso_front_mat = base_mat * Mat4::::from(self.torso_front); + let torso_back_mat = torso_front_mat * Mat4::::from(self.torso_back); + let head_lower_mat = torso_front_mat * Mat4::::from(self.head_lower); + let leg_fl_mat = torso_front_mat * Mat4::::from(self.leg_fl); + let leg_fr_mat = torso_front_mat * Mat4::::from(self.leg_fr); + let leg_bl_mat = torso_back_mat * Mat4::::from(self.leg_bl); + let leg_br_mat = torso_back_mat * Mat4::::from(self.leg_br); + let head_upper_mat = head_lower_mat * Mat4::::from(self.head_upper); - ( - [ - FigureBoneData::new(torso_front_mat * head_lower_mat * head_upper_mat), - FigureBoneData::new(torso_front_mat * head_lower_mat), - FigureBoneData::new( - torso_front_mat - * head_lower_mat - * head_upper_mat - * self.jaw.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat * torso_back_mat * self.tail.compute_base_matrix(), - ), - FigureBoneData::new(torso_front_mat), - FigureBoneData::new(torso_front_mat * torso_back_mat), - FigureBoneData::new(torso_front_mat * head_lower_mat * head_upper_mat * ears_mat), - FigureBoneData::new(torso_front_mat * leg_fl_mat), - FigureBoneData::new(torso_front_mat * leg_fr_mat), - FigureBoneData::new(torso_front_mat * torso_back_mat * leg_bl_mat), - FigureBoneData::new(torso_front_mat * torso_back_mat * leg_br_mat), - FigureBoneData::new( - torso_front_mat * leg_fl_mat * self.foot_fl.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat * leg_fr_mat * self.foot_fr.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat - * torso_back_mat - * leg_bl_mat - * self.foot_bl.compute_base_matrix(), - ), - FigureBoneData::new( - torso_front_mat - * torso_back_mat - * leg_br_mat - * self.foot_br.compute_base_matrix(), - ), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } - - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head_upper.interpolate(&target.head_upper, dt); - self.head_lower.interpolate(&target.head_lower, dt); - self.jaw.interpolate(&target.jaw, dt); - self.tail.interpolate(&target.tail, dt); - self.torso_back.interpolate(&target.torso_back, dt); - self.torso_front.interpolate(&target.torso_front, dt); - self.ears.interpolate(&target.ears, dt); - self.leg_fl.interpolate(&target.leg_fl, dt); - self.leg_fr.interpolate(&target.leg_fr, dt); - self.leg_bl.interpolate(&target.leg_bl, dt); - self.leg_br.interpolate(&target.leg_br, dt); - self.foot_fl.interpolate(&target.foot_fl, dt); - self.foot_fr.interpolate(&target.foot_fr, dt); - self.foot_bl.interpolate(&target.foot_bl, dt); - self.foot_br.interpolate(&target.foot_br, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(head_upper_mat), + make_bone(head_lower_mat), + make_bone(head_upper_mat * Mat4::::from(self.jaw)), + make_bone(torso_back_mat * Mat4::::from(self.tail)), + make_bone(torso_front_mat), + make_bone(torso_back_mat), + make_bone(head_upper_mat * Mat4::::from(self.ears)), + make_bone(leg_fl_mat), + make_bone(leg_fr_mat), + make_bone(leg_bl_mat), + make_bone(leg_br_mat), + make_bone(leg_fl_mat * Mat4::::from(self.foot_fl)), + make_bone(leg_fr_mat * Mat4::::from(self.foot_fr)), + make_bone(leg_bl_mat * Mat4::::from(self.foot_bl)), + make_bone(leg_br_mat * Mat4::::from(self.foot_br)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/quadruped_medium/run.rs b/voxygen/src/anim/src/quadruped_medium/run.rs index 5dadddf37d..8e12e0ee01 100644 --- a/voxygen/src/anim/src/quadruped_medium/run.rs +++ b/voxygen/src/anim/src/quadruped_medium/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedMediumSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -59,7 +61,7 @@ impl Animation for RunAnimation { // let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -73,247 +75,248 @@ impl Animation for RunAnimation { let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); if speed < 8.0 { //Trot - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(short * -0.03 - 0.1 + x_tilt * -0.5) - * Quaternion::rotation_z(tilt * -1.2); + next.head_upper.orientation = + Quaternion::rotation_x(short * -0.03 - 0.1 + x_tilt * -0.5) + * Quaternion::rotation_z(tilt * -1.2); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(tilt * -0.8) + next.head_lower.orientation = Quaternion::rotation_z(tilt * -0.8) * Quaternion::rotation_x(short * -0.05 + x_tilt * -0.5); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(0.0); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(shortalt * 0.3) * Quaternion::rotation_z(tilt * 1.5); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1 + shortalt * 1.0 + x_tilt, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_x(short * 0.03 + x_tilt) + next.torso_front.orientation = Quaternion::rotation_x(short * 0.03 + x_tilt) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(tilt * -1.5); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = Vec3::new( + next.torso_back.position = Vec3::new( 0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1 + shortalt * 0.04 - 0.2, ); - next.torso_back.ori = + next.torso_back.orientation = Quaternion::rotation_x(short * 0.06) * Quaternion::rotation_z(tilt * 1.8); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(shortalt * 0.04 + 0.2); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(shortalt * 0.04 + 0.2); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertaltfslow * -1.4, skeleton_attr.leg_f.2 + 1.0 + footverttaltfslow * -0.3, ); - next.leg_fl.ori = Quaternion::rotation_x(footverttaltfslow * -0.35) + next.leg_fl.orientation = Quaternion::rotation_x(footverttaltfslow * -0.35) * Quaternion::rotation_z(tilt * -0.5); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertalt * -1.4, skeleton_attr.leg_f.2 + 1.0 + footverttalt * -0.3, ); - next.leg_fr.ori = + next.leg_fr.orientation = Quaternion::rotation_x(footverttalt * -0.35) * Quaternion::rotation_z(tilt * -0.5); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvertalt * -1.0, skeleton_attr.leg_b.2 + 1.0 + footverttalt * -0.3, ); - next.leg_bl.ori = + next.leg_bl.orientation = Quaternion::rotation_x(footverttalt * -0.2) * Quaternion::rotation_z(tilt * -1.5); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvertaltfslow * -1.0, skeleton_attr.leg_b.2 + 1.0 + footverttaltfslow * -0.3, ); - next.leg_br.ori = Quaternion::rotation_x(footverttaltfslow * -0.2) + next.leg_br.orientation = Quaternion::rotation_x(footverttaltfslow * -0.2) * Quaternion::rotation_z(tilt * -1.5); next.leg_br.scale = Vec3::one() * 1.02; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvertfslow * -1.0 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fl.ori = + next.foot_fl.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footverttfslow * 0.5); next.foot_fl.scale = Vec3::one() * 0.96; - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvert * -1.0 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fr.ori = + next.foot_fr.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footvertt * 0.5); next.foot_fr.scale = Vec3::one() * 0.96; - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvert * -1.8).max(0.0)), ); - next.foot_bl.ori = Quaternion::rotation_x(footvertt * 0.5 - 0.2); + next.foot_bl.orientation = Quaternion::rotation_x(footvertt * 0.5 - 0.2); next.foot_bl.scale = Vec3::one() * 0.96; - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvertfslow * -0.8).max(-0.0)), ); - next.foot_br.ori = Quaternion::rotation_x(footverttfslow * 0.5 - 0.2); + next.foot_br.orientation = Quaternion::rotation_x(footverttfslow * 0.5 - 0.2); next.foot_br.scale = Vec3::one() * 0.96; } else { let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); //Gallop - next.head_upper.offset = + next.head_upper.position = Vec3::new(0.0, skeleton_attr.head_upper.0, skeleton_attr.head_upper.1); - next.head_upper.ori = Quaternion::rotation_x(short * -0.03 - 0.1) + next.head_upper.orientation = Quaternion::rotation_x(short * -0.03 - 0.1) * Quaternion::rotation_z(tilt * -1.2) * Quaternion::rotation_y(tilt * 0.8); next.head_upper.scale = Vec3::one(); - next.head_lower.offset = + next.head_lower.position = Vec3::new(0.0, skeleton_attr.head_lower.0, skeleton_attr.head_lower.1); - next.head_lower.ori = Quaternion::rotation_z(tilt * -0.8) + next.head_lower.orientation = Quaternion::rotation_z(tilt * -0.8) * Quaternion::rotation_x(short * -0.05) * Quaternion::rotation_y(tilt * 0.3); next.head_lower.scale = Vec3::one() * 1.02; - next.jaw.offset = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); - next.jaw.ori = Quaternion::rotation_x(0.0); + next.jaw.position = Vec3::new(0.0, skeleton_attr.jaw.0, skeleton_attr.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(0.0); next.jaw.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(shortalt * 0.3) * Quaternion::rotation_z(tilt * 1.5); next.tail.scale = Vec3::one(); - next.torso_front.offset = Vec3::new( + next.torso_front.position = Vec3::new( 0.0, skeleton_attr.torso_front.0, skeleton_attr.torso_front.1 + shortalt * 2.5 + x_tilt * 10.0, ) * skeleton_attr.scaler / 11.0; - next.torso_front.ori = Quaternion::rotation_x(short * 0.13 + x_tilt) + next.torso_front.orientation = Quaternion::rotation_x(short * 0.13 + x_tilt) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_z(tilt * -1.5); next.torso_front.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.torso_back.offset = Vec3::new( + next.torso_back.position = Vec3::new( 0.0, skeleton_attr.torso_back.0, skeleton_attr.torso_back.1 + shortalt * 0.2 - 0.2, ); - next.torso_back.ori = Quaternion::rotation_x(short * 0.1) + next.torso_back.orientation = Quaternion::rotation_x(short * 0.1) * Quaternion::rotation_z(tilt * 1.8) * Quaternion::rotation_y(tilt * 0.6); next.torso_back.scale = Vec3::one(); - next.ears.offset = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); - next.ears.ori = Quaternion::rotation_x(shortalt * 0.2 + 0.2); + next.ears.position = Vec3::new(0.0, skeleton_attr.ears.0, skeleton_attr.ears.1); + next.ears.orientation = Quaternion::rotation_x(shortalt * 0.2 + 0.2); next.ears.scale = Vec3::one() * 1.02; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertaltf * -1.3, skeleton_attr.leg_f.2 + 1.0 + footverttaltf * -1.9, ); - next.leg_fl.ori = Quaternion::rotation_x(footverttaltf * -0.65) + next.leg_fl.orientation = Quaternion::rotation_x(footverttaltf * -0.65) * Quaternion::rotation_z(tilt * -0.5) * Quaternion::rotation_y(tilt * 1.5); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.leg_f.0, skeleton_attr.leg_f.1 + footvertalt * -1.3, skeleton_attr.leg_f.2 + 1.0 + footverttalt * -1.9, ); - next.leg_fr.ori = Quaternion::rotation_x(footverttalt * -0.65) + next.leg_fr.orientation = Quaternion::rotation_x(footverttalt * -0.65) * Quaternion::rotation_z(tilt * -0.5) * Quaternion::rotation_y(tilt * 1.5); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvert * -1.7, skeleton_attr.leg_b.2 + 1.0 + footvertt * -1.5, ); - next.leg_bl.ori = Quaternion::rotation_x(footvertt * -0.45 - 0.2) + next.leg_bl.orientation = Quaternion::rotation_x(footvertt * -0.45 - 0.2) * Quaternion::rotation_y(tilt * 1.5) * Quaternion::rotation_z(tilt * -1.5); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.leg_b.0, skeleton_attr.leg_b.1 + footvertf * -1.7, skeleton_attr.leg_b.2 + 1.0 + footverttf * -1.5, ); - next.leg_br.ori = Quaternion::rotation_x(footverttf * -0.45 - 0.2) + next.leg_br.orientation = Quaternion::rotation_x(footverttf * -0.45 - 0.2) * Quaternion::rotation_y(tilt * 1.5) * Quaternion::rotation_z(tilt * -1.5); next.leg_br.scale = Vec3::one() * 1.02; - next.foot_fl.offset = Vec3::new( + next.foot_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvertf * -2.7 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fl.ori = + next.foot_fl.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footverttf * 0.9) * Quaternion::rotation_y(tilt * -1.0); next.foot_fl.scale = Vec3::one() * 0.96; - next.foot_fr.offset = Vec3::new( + next.foot_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + ((footvert * -2.7 * skeleton_attr.maximize).max(0.0)), ); - next.foot_fr.ori = + next.foot_fr.orientation = Quaternion::rotation_x((1.0 - skeleton_attr.dampen) * -1.0 + footvertt * 0.9) * Quaternion::rotation_y(tilt * -1.0); next.foot_fr.scale = Vec3::one() * 0.96; - next.foot_bl.offset = Vec3::new( + next.foot_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvert * 1.3).max(0.0)), ); - next.foot_bl.ori = Quaternion::rotation_x(footvertt * -0.9 - 0.2) + next.foot_bl.orientation = Quaternion::rotation_x(footvertt * -0.9 - 0.2) * Quaternion::rotation_y(tilt * -1.0); next.foot_bl.scale = Vec3::one() * 0.96; - next.foot_br.offset = Vec3::new( + next.foot_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + ((footvertf * 1.3).max(-0.0)), ); - next.foot_br.ori = Quaternion::rotation_x(footverttf * -0.9 - 0.2) + next.foot_br.orientation = Quaternion::rotation_x(footverttf * -0.9 - 0.2) * Quaternion::rotation_y(tilt * -1.0); next.foot_br.scale = Vec3::one() * 0.96; } diff --git a/voxygen/src/anim/src/quadruped_small/feed.rs b/voxygen/src/anim/src/quadruped_small/feed.rs index aa4de97cb3..348eb5caf8 100644 --- a/voxygen/src/anim/src/quadruped_small/feed.rs +++ b/voxygen/src/anim/src/quadruped_small/feed.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct FeedAnimation; @@ -39,60 +41,62 @@ impl Animation for FeedAnimation { * 0.5, ); - next.head.offset = Vec3::new( + next.head.position = Vec3::new( 0.0, skeleton_attr.head.0 + 1.5, skeleton_attr.head.1 + slow * 0.2, ); - next.head.ori = Quaternion::rotation_z(head_look.y) + next.head.orientation = Quaternion::rotation_z(head_look.y) * Quaternion::rotation_x(slow * 0.05 + quick * 0.08 - 0.4 * skeleton_attr.feed); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(slow * 0.02, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(slow * 0.02, skeleton_attr.chest.0, skeleton_attr.chest.1) / 11.0 * skeleton_attr.scaler; - next.chest.ori = Quaternion::rotation_x(-0.35 * skeleton_attr.feed) + next.chest.orientation = Quaternion::rotation_x(-0.35 * skeleton_attr.feed) * Quaternion::rotation_y(head_look.y * 0.1); next.chest.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + 0.5, ); - next.leg_fl.ori = Quaternion::rotation_x(slow * 0.01 + 0.25 * skeleton_attr.feed) + next.leg_fl.orientation = Quaternion::rotation_x(slow * 0.01 + 0.25 * skeleton_attr.feed) * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + 0.5, ); - next.leg_fr.ori = Quaternion::rotation_x(slow_alt * 0.01 + 0.25 * skeleton_attr.feed) - * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); + next.leg_fr.orientation = + Quaternion::rotation_x(slow_alt * 0.01 + 0.25 * skeleton_attr.feed) + * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + 1.0, skeleton_attr.feet_b.2 - 1.0, ); - next.leg_bl.ori = Quaternion::rotation_x(slow_alt * 0.01 + 0.15 * skeleton_attr.feed) - * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); + next.leg_bl.orientation = + Quaternion::rotation_x(slow_alt * 0.01 + 0.15 * skeleton_attr.feed) + * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + 1.0, skeleton_attr.feet_b.2 - 1.0, ); - next.leg_br.ori = Quaternion::rotation_x(slow * 0.01 + 0.15 * skeleton_attr.feed) + next.leg_br.orientation = Quaternion::rotation_x(slow * 0.01 + 0.15 * skeleton_attr.feed) * Quaternion::rotation_y(slow * -0.02 - head_look.y * 0.1); next.leg_br.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(slow * 0.3 + head_look.y * 0.3); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(slow * 0.3 + head_look.y * 0.3); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_small/idle.rs b/voxygen/src/anim/src/quadruped_small/idle.rs index d32c14a128..9209704c2d 100644 --- a/voxygen/src/anim/src/quadruped_small/idle.rs +++ b/voxygen/src/anim/src/quadruped_small/idle.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; use std::{f32::consts::PI, ops::Mul}; -use vek::*; pub struct IdleAnimation; @@ -38,55 +40,56 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.2); - next.head.ori = Quaternion::rotation_z(head_look.x) + next.head.position = + Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.2); + next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y + slow_alt * 0.03); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(slow * 0.05, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(slow * 0.05, skeleton_attr.chest.0, skeleton_attr.chest.1) / 11.0 * skeleton_attr.scaler; - next.chest.ori = Quaternion::rotation_y(slow * 0.05); + next.chest.orientation = Quaternion::rotation_y(slow * 0.05); next.chest.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slow * -0.2, ); - next.leg_fl.ori = + next.leg_fl.orientation = Quaternion::rotation_x(slow * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2 + slow * 0.2, ); - next.leg_fr.ori = + next.leg_fr.orientation = Quaternion::rotation_x(slow_alt * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slow * -0.2, ); - next.leg_bl.ori = + next.leg_bl.orientation = Quaternion::rotation_x(slow_alt * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2 + slow * 0.2, ); - next.leg_br.ori = + next.leg_br.orientation = Quaternion::rotation_x(slow * 0.03) * Quaternion::rotation_y(slow * -0.05); next.leg_br.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_z(slow * 0.4); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_z(slow * 0.4); next.tail.scale = Vec3::one(); next diff --git a/voxygen/src/anim/src/quadruped_small/jump.rs b/voxygen/src/anim/src/quadruped_small/jump.rs index a8395ac958..8315f0bbf9 100644 --- a/voxygen/src/anim/src/quadruped_small/jump.rs +++ b/voxygen/src/anim/src/quadruped_small/jump.rs @@ -1,5 +1,7 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; -use vek::*; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; pub struct JumpAnimation; @@ -20,50 +22,50 @@ impl Animation for JumpAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.5); + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.5); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1) * skeleton_attr.scaler / 11.0; - next.chest.ori = Quaternion::rotation_y(0.0); + next.chest.orientation = Quaternion::rotation_y(0.0); next.chest.scale = Vec3::one() * skeleton_attr.scaler / 11.0; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.leg_fl.ori = Quaternion::rotation_x(0.0); + next.leg_fl.orientation = Quaternion::rotation_x(0.0); next.leg_fl.scale = Vec3::one(); - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ); - next.leg_fr.ori = Quaternion::rotation_x(0.0); + next.leg_fr.orientation = Quaternion::rotation_x(0.0); next.leg_fr.scale = Vec3::one(); - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.leg_bl.ori = Quaternion::rotation_x(0.0); + next.leg_bl.orientation = Quaternion::rotation_x(0.0); next.leg_bl.scale = Vec3::one(); - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ); - next.leg_br.ori = Quaternion::rotation_x(0.0); + next.leg_br.orientation = Quaternion::rotation_x(0.0); next.leg_br.scale = Vec3::one(); - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(-0.3); + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(-0.3); next.tail.scale = Vec3::one(); next } diff --git a/voxygen/src/anim/src/quadruped_small/mod.rs b/voxygen/src/anim/src/quadruped_small/mod.rs index 0c19fc6ed2..05142e0639 100644 --- a/voxygen/src/anim/src/quadruped_small/mod.rs +++ b/voxygen/src/anim/src/quadruped_small/mod.rs @@ -6,67 +6,45 @@ pub mod run; // Reexports pub use self::{feed::FeedAnimation, idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation}; -use super::{Bone, FigureBoneData, Skeleton}; +use super::{make_bone, vek::*, Bone, FigureBoneData, Skeleton}; use common::comp::{self}; -use vek::Vec3; +use core::convert::TryFrom; -#[derive(Clone, Default)] -pub struct QuadrupedSmallSkeleton { - head: Bone, - chest: Bone, - leg_fl: Bone, - leg_fr: Bone, - leg_bl: Bone, - leg_br: Bone, - tail: Bone, -} - -impl QuadrupedSmallSkeleton { - pub fn new() -> Self { Self::default() } -} +skeleton_impls!(struct QuadrupedSmallSkeleton { + + head, + + chest, + + leg_fl, + + leg_fr, + + leg_bl, + + leg_br, + + tail, +}); impl Skeleton for QuadrupedSmallSkeleton { type Attr = SkeletonAttr; + const BONE_COUNT: usize = 7; #[cfg(feature = "use-dyn-lib")] const COMPUTE_FN: &'static [u8] = b"quadruped_small_compute_mats\0"; - fn bone_count(&self) -> usize { 7 } - #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_small_compute_mats")] - fn compute_matrices_inner(&self) -> ([FigureBoneData; 16], Vec3) { - let chest_mat = self.chest.compute_base_matrix(); - ( - [ - FigureBoneData::new(chest_mat * self.head.compute_base_matrix()), - FigureBoneData::new(chest_mat), - FigureBoneData::new(chest_mat * self.leg_fl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.leg_fr.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.leg_bl.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.leg_br.compute_base_matrix()), - FigureBoneData::new(chest_mat * self.tail.compute_base_matrix()), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - FigureBoneData::default(), - ], - Vec3::default(), - ) - } + fn compute_matrices_inner( + &self, + base_mat: Mat4, + buf: &mut [FigureBoneData; super::MAX_BONE_COUNT], + ) -> Vec3 { + let chest_mat = base_mat * Mat4::::from(self.chest); - fn interpolate(&mut self, target: &Self, dt: f32) { - self.head.interpolate(&target.head, dt); - self.chest.interpolate(&target.chest, dt); - self.leg_fl.interpolate(&target.leg_fl, dt); - self.leg_fr.interpolate(&target.leg_fr, dt); - self.leg_bl.interpolate(&target.leg_bl, dt); - self.leg_br.interpolate(&target.leg_br, dt); - self.tail.interpolate(&target.tail, dt); + *(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [ + make_bone(chest_mat * Mat4::::from(self.head)), + make_bone(chest_mat), + make_bone(chest_mat * Mat4::::from(self.leg_fl)), + make_bone(chest_mat * Mat4::::from(self.leg_fr)), + make_bone(chest_mat * Mat4::::from(self.leg_bl)), + make_bone(chest_mat * Mat4::::from(self.leg_br)), + make_bone(chest_mat * Mat4::::from(self.tail)), + ]; + Vec3::default() } } diff --git a/voxygen/src/anim/src/quadruped_small/run.rs b/voxygen/src/anim/src/quadruped_small/run.rs index 09d14dedc0..cbf1092460 100644 --- a/voxygen/src/anim/src/quadruped_small/run.rs +++ b/voxygen/src/anim/src/quadruped_small/run.rs @@ -1,6 +1,8 @@ -use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use super::{ + super::{vek::*, Animation}, + QuadrupedSmallSkeleton, SkeletonAttr, +}; use std::f32::consts::PI; -use vek::*; pub struct RunAnimation; @@ -35,7 +37,7 @@ impl Animation for RunAnimation { let ori: Vec2 = Vec2::from(orientation); let last_ori = Vec2::from(last_ori); - let tilt = if Vec2::new(ori, last_ori) + let tilt = if ::vek::Vec2::new(ori, last_ori) .map(|o| o.magnitude_squared()) .map(|m| m > 0.001 && m.is_finite()) .reduce_and() @@ -48,13 +50,13 @@ impl Animation for RunAnimation { } * 1.3; let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude()); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_x(x_tilt * -0.5 + short * -0.2) + next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); + next.head.orientation = Quaternion::rotation_x(x_tilt * -0.5 + short * -0.2) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_z(tilt * -1.2); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new( + next.chest.position = Vec3::new( 0.0, skeleton_attr.chest.0, skeleton_attr.chest.1 @@ -62,54 +64,58 @@ impl Animation for RunAnimation { + shortalt * 3.0 * skeleton_attr.spring, ) / 11.0 * skeleton_attr.scaler; - next.chest.ori = Quaternion::rotation_x(short * 0.2 * skeleton_attr.spring + x_tilt) - * Quaternion::rotation_y(tilt * 0.8) - * Quaternion::rotation_z(tilt * -1.5); + next.chest.orientation = + Quaternion::rotation_x(short * 0.2 * skeleton_attr.spring + x_tilt) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_z(tilt * -1.5); next.chest.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - next.leg_fl.offset = Vec3::new( + next.leg_fl.position = Vec3::new( -skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + footverttf * 3.0 * skeleton_attr.maximize, skeleton_attr.feet_f.2 + ((footvertf * -1.5).max(-1.0)), ); - next.leg_fl.ori = Quaternion::rotation_x(0.2 + skeleton_attr.minimize * footverttf * 0.65) - * Quaternion::rotation_z(tilt * -0.5) - * Quaternion::rotation_y(tilt * 1.5); + next.leg_fl.orientation = + Quaternion::rotation_x(0.2 + skeleton_attr.minimize * footverttf * 0.65) + * Quaternion::rotation_z(tilt * -0.5) + * Quaternion::rotation_y(tilt * 1.5); next.leg_fl.scale = Vec3::one() * 1.02; - next.leg_fr.offset = Vec3::new( + next.leg_fr.position = Vec3::new( skeleton_attr.feet_f.0, skeleton_attr.feet_f.1 + footvertt * 3.0 * skeleton_attr.minimize, skeleton_attr.feet_f.2 + ((footvert * -1.5).max(-1.0)), ); - next.leg_fr.ori = Quaternion::rotation_x(0.2 + skeleton_attr.maximize * footvertt * 0.65) - * Quaternion::rotation_z(tilt * -0.5) - * Quaternion::rotation_y(tilt * 1.5); + next.leg_fr.orientation = + Quaternion::rotation_x(0.2 + skeleton_attr.maximize * footvertt * 0.65) + * Quaternion::rotation_z(tilt * -0.5) + * Quaternion::rotation_y(tilt * 1.5); next.leg_fr.scale = Vec3::one() * 1.02; - next.leg_bl.offset = Vec3::new( + next.leg_bl.position = Vec3::new( -skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + footvertt * -1.4, skeleton_attr.feet_b.2 + ((footvert * 1.5).max(-1.0)), ); - next.leg_bl.ori = Quaternion::rotation_x(-0.25 + skeleton_attr.maximize * footvertt * -0.8) - * Quaternion::rotation_y(tilt * 1.5) - * Quaternion::rotation_z(tilt * -1.5); + next.leg_bl.orientation = + Quaternion::rotation_x(-0.25 + skeleton_attr.maximize * footvertt * -0.8) + * Quaternion::rotation_y(tilt * 1.5) + * Quaternion::rotation_z(tilt * -1.5); next.leg_bl.scale = Vec3::one() * 1.02; - next.leg_br.offset = Vec3::new( + next.leg_br.position = Vec3::new( skeleton_attr.feet_b.0, skeleton_attr.feet_b.1 + footverttf * -1.4, skeleton_attr.feet_b.2 + ((footvertf * 1.5).max(-1.0)), ); - next.leg_br.ori = + next.leg_br.orientation = Quaternion::rotation_x(-0.25 + skeleton_attr.maximize * footverttf * -0.8) * Quaternion::rotation_y(tilt * 1.5) * Quaternion::rotation_z(tilt * -1.5); next.leg_br.scale = Vec3::one() * 1.02; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); - next.tail.ori = Quaternion::rotation_x(short * 0.2 + x_tilt) + next.tail.position = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); + next.tail.orientation = Quaternion::rotation_x(short * 0.2 + x_tilt) * Quaternion::rotation_y(tilt * 0.8) * Quaternion::rotation_z(tilt * 1.5); next.tail.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/vek.rs b/voxygen/src/anim/src/vek.rs new file mode 100644 index 0000000000..ee9bda3c45 --- /dev/null +++ b/voxygen/src/anim/src/vek.rs @@ -0,0 +1,8 @@ +pub use ::vek::{ + bezier::repr_simd::*, geom::repr_simd::*, mat::repr_simd::column_major::Mat4, ops::*, + quaternion::repr_simd::*, transform::repr_simd::*, transition::*, vec::repr_simd::*, +}; +/* pub use ::vek::{ + bezier::repr_c::*, geom::repr_c::*, mat::repr_c::column_major::Mat4, ops::*, + quaternion::repr_c::*, transform::repr_c::*, transition::*, vec::repr_c::*, +}; */ diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index a8796fe8e8..5dab15fd4f 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -44,22 +44,18 @@ pub struct AudioFrontend { listener: Listener, } -#[allow(clippy::same_item_push)] // TODO: Pending review in #587 impl AudioFrontend { /// Construct with given device - #[allow(clippy::redundant_clone)] // TODO: Pending review in #587 pub fn new(device: String, max_sfx_channels: usize) -> Self { - let mut sfx_channels = Vec::with_capacity(max_sfx_channels); let audio_device = get_device_raw(&device); + let mut sfx_channels = Vec::with_capacity(max_sfx_channels); if let Some(audio_device) = &audio_device { - for _ in 0..max_sfx_channels { - sfx_channels.push(SfxChannel::new(&audio_device)); - } + sfx_channels.resize_with(max_sfx_channels, || SfxChannel::new(&audio_device)); } Self { - device: device.clone(), + device, device_list: list_devices(), audio_device, sound_cache: SoundCache::default(), diff --git a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs index 3caeb17fc4..efc36f6997 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs @@ -53,7 +53,8 @@ impl EventMapper for CombatEventMapper { let sfx_event_bus = ecs.read_resource::>(); let mut sfx_emitter = sfx_event_bus.emitter(); - let cam_pos = camera.dependents().cam_pos; + let focus_off = camera.get_focus_pos().map(f32::trunc); + let cam_pos = camera.dependents().cam_pos + focus_off; for (entity, pos, loadout, character) in ( &ecs.entities(), diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 55bff58c96..f551987ebb 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -50,7 +50,8 @@ impl EventMapper for MovementEventMapper { let sfx_event_bus = ecs.read_resource::>(); let mut sfx_emitter = sfx_event_bus.emitter(); - let cam_pos = camera.dependents().cam_pos; + let focus_off = camera.get_focus_pos().map(f32::trunc); + let cam_pos = camera.dependents().cam_pos + focus_off; for (entity, pos, vel, body, physics, character) in ( &ecs.entities(), diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 8c47867aa1..7439e24e72 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -234,8 +234,10 @@ impl SfxMgr { } let ecs = state.ecs(); + let focus_off = camera.get_focus_pos().map(f32::trunc); + let cam_pos = camera.dependents().cam_pos + focus_off; - audio.set_listener_pos(camera.dependents().cam_pos, camera.dependents().cam_dir); + audio.set_listener_pos(cam_pos, camera.dependents().cam_dir); // TODO: replace; deprecated in favor of outcomes self.event_mapper @@ -247,7 +249,7 @@ impl SfxMgr { for event in events { let position = match event.pos { Some(pos) => pos, - _ => camera.dependents().cam_pos, + _ => cam_pos, }; if let Some(item) = self.triggers.get_trigger(&event.sfx) { diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 48e99b22b9..cd98cee9eb 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -52,7 +52,7 @@ enum ImageSpec { impl ImageSpec { fn create_graphic(&self) -> Graphic { match self { - ImageSpec::Png(specifier) => Graphic::Image(graceful_load_img(&specifier)), + ImageSpec::Png(specifier) => Graphic::Image(graceful_load_img(&specifier), None), ImageSpec::Vox(specifier) => Graphic::Voxel( graceful_load_segment_no_skin(&specifier), Transform { diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index b6871ff88e..ae34f752d0 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -134,7 +134,7 @@ impl<'a> Widget for Map<'a> { .set(state.ids.icon, ui); // Map Title - Text::new(&self.localized_strings.get("hud.map.map_title")) + Text::new(self.localized_strings.get("hud.map.map_title")) .mid_top_with_margin_on(state.ids.frame, 3.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(29)) @@ -142,15 +142,12 @@ impl<'a> Widget for Map<'a> { .set(state.ids.map_title, ui); // Questlog Title - Text::new(&format!( - "{}", - &self.localized_strings.get("hud.map.qlog_title") - )) - .mid_top_with_margin_on(state.ids.qlog_align, 6.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(21)) - .color(TEXT_COLOR) - .set(state.ids.qlog_title, ui); + Text::new(self.localized_strings.get("hud.map.qlog_title")) + .mid_top_with_margin_on(state.ids.qlog_align, 6.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(21)) + .color(TEXT_COLOR) + .set(state.ids.qlog_title, ui); // X-Button if Button::image(self.imgs.close_button) @@ -197,8 +194,10 @@ impl<'a> Widget for Map<'a> { .read_storage::() .get(self.client.entity()) .map_or(Vec3::zero(), |pos| pos.0); - let w_src = worldsize.x / TerrainChunkSize::RECT_SIZE.x as f64 / zoom; - let h_src = worldsize.y / TerrainChunkSize::RECT_SIZE.y as f64 / zoom; + let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .reduce_partial_max()/*.min(f64::MAX)*/; + let w_src = max_zoom / zoom; + let h_src = max_zoom / zoom; let rect_src = position::Rect::from_xy_dim( [ player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64, diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 28d27563e7..502ee619e0 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -134,7 +134,7 @@ impl<'a> Widget for MiniMap<'a> { // somehow if you zoom in too far. Or both. let min_zoom = 1.0; let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .reduce_partial_min()/*.min(f64::MAX)*/; + .reduce_partial_max()/*.min(f64::MAX)*/; // NOTE: Not sure if a button can be clicked while disabled, but we still double // check for both kinds of zoom to make sure that not only was the @@ -190,8 +190,8 @@ impl<'a> Widget for MiniMap<'a> { .map_or(Vec3::zero(), |pos| pos.0); // Get map image source rectangle dimensons. - let w_src = worldsize.x / TerrainChunkSize::RECT_SIZE.x as f64 / zoom; - let h_src = worldsize.y / TerrainChunkSize::RECT_SIZE.y as f64 / zoom; + let w_src = max_zoom / zoom; + let h_src = max_zoom / zoom; // Set map image to be centered around player coordinates. let rect_src = position::Rect::from_xy_dim( diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index bf52c29b1e..202aa2dc2b 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -46,8 +46,11 @@ use spell::Spell; use crate::{ ecs::comp as vcomp, i18n::{i18n_asset_key, LanguageMetadata, VoxygenLocalization}, - render::{AaMode, CloudMode, Consts, FluidMode, Globals, Renderer}, - scene::camera::{self, Camera}, + render::{Consts, Globals, RenderMode, Renderer}, + scene::{ + camera::{self, Camera}, + lod, + }, ui::{fonts::ConrodVoxygenFonts, slot, Graphic, Ingameable, ScaleMode, Ui}, window::{Event as WinEvent, GameInput}, GlobalState, @@ -178,6 +181,7 @@ widget_ids! { time, entity_count, num_chunks, + num_lights, num_figures, num_particles, @@ -245,7 +249,9 @@ pub struct DebugInfo { pub velocity: Option, pub ori: Option, pub num_chunks: u32, + pub num_lights: u32, pub num_visible_chunks: u32, + pub num_shadow_chunks: u32, pub num_figures: u32, pub num_figures_visible: u32, pub num_particles: u32, @@ -268,6 +274,7 @@ pub enum Event { ToggleMouseYInvert(bool), ToggleSmoothPan(bool), AdjustViewDistance(u32), + AdjustLodDetail(u32), AdjustSpriteRenderDistance(u32), AdjustFigureLoDRenderDistance(u32), AdjustMusicVolume(f32), @@ -280,9 +287,6 @@ pub enum Event { AdjustWindowSize([u16; 2]), ToggleParticlesEnabled(bool), ToggleFullscreen, - ChangeAaMode(AaMode), - ChangeCloudMode(CloudMode), - ChangeFluidMode(FluidMode), ChangeResolution([u16; 2]), ChangeBitDepth(Option), ChangeRefreshRate(Option), @@ -305,14 +309,15 @@ pub enum Event { UseSlot(comp::slot::Slot), SwapSlots(comp::slot::Slot, comp::slot::Slot), DropSlot(comp::slot::Slot), - ChangeHotbarState(HotbarState), + ChangeHotbarState(Box), Ability3(bool), Logout, Quit, - ChangeLanguage(LanguageMetadata), + ChangeLanguage(Box), ChangeBinding(GameInput), ResetBindings, ChangeFreeLookBehavior(PressBehavior), + ChangeRenderMode(Box), ChangeAutoWalkBehavior(PressBehavior), ChangeStopAutoWalkOnInput(bool), CraftRecipe(String), @@ -577,10 +582,17 @@ impl Hud { ui.set_scaling_mode(settings.gameplay.ui_scale); // Generate ids. let ids = Ids::new(ui.id_generator()); + // NOTE: Use a border the same color as the LOD ocean color (but with a + // translucent alpha since UI have transparency and LOD doesn't). + let mut water_color = lod::water_color(); + water_color.a = 0.5; // Load world map let world_map = ( - ui.add_graphic_with_rotations(Graphic::Image(client.world_map.0.clone())), - client.world_map.1, + ui.add_graphic_with_rotations(Graphic::Image( + client.world_map.0.clone(), + Some(water_color), + )), + client.world_map.1.map(u32::from), ); // Load images. let imgs = Imgs::load(&mut ui).expect("Failed to load images!"); @@ -670,7 +682,7 @@ impl Hud { &mut self, client: &Client, global_state: &GlobalState, - debug_info: DebugInfo, + debug_info: &Option, dt: Duration, info: HudInfo, camera: &Camera, @@ -679,10 +691,6 @@ impl Hud { let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets(); // pulse time for pulsating elements self.pulse = self.pulse + dt.as_secs_f32(); - self.velocity = match debug_info.velocity { - Some(velocity) => velocity.0.magnitude(), - None => 0.0, - }; let version = format!( "{}-{}", @@ -776,7 +784,7 @@ impl Hud { } // Max amount the sct font size increases when "flashing" - const FLASH_MAX: f32 = 25.0; + const FLASH_MAX: u32 = 2; // Get player position. let player_pos = client @@ -820,9 +828,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if timer < 0.1 { - (FLASH_MAX * (1.0 - timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -873,9 +881,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if floater.timer < 0.1 { - (FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -955,7 +963,7 @@ impl Hud { + ((exp_change.abs() as f32 / stats.exp.maximum() as f32).min(1.0) * 50.0) as u32 + if timer < 0.1 { - (FLASH_MAX * (1.0 - timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -999,7 +1007,7 @@ impl Hud { .min(1.0) * 50.0) as u32 + if floater.timer < 0.1 { - (FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -1228,9 +1236,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if timer < 0.1 { - (FLASH_MAX * (1.0 - timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -1273,9 +1281,9 @@ impl Hud { // Increase font size based on fraction of maximum health // "flashes" by having a larger size in the first 100ms let font_size = 30 - + (max_hp_frac * 30.0) as u32 + + ((max_hp_frac * 10.0) as u32) * 3 + if floater.timer < 0.1 { - (FLASH_MAX * (1.0 - floater.timer / 0.1)) as u32 + FLASH_MAX * (((1.0 - floater.timer / 0.1) * 10.0) as u32) } else { 0 }; @@ -1383,7 +1391,11 @@ impl Hud { } // Display debug window. - if global_state.settings.gameplay.toggle_debug { + if let Some(debug_info) = debug_info { + self.velocity = match debug_info.velocity { + Some(velocity) => velocity.0.magnitude(), + None => 0.0, + }; // Alpha Version Text::new(&version) .top_left_with_margins_on(ui_widgets.window, 5.0, 5.0) @@ -1489,8 +1501,8 @@ impl Hud { // Number of chunks Text::new(&format!( - "Chunks: {} ({} visible)", - debug_info.num_chunks, debug_info.num_visible_chunks, + "Chunks: {} ({} visible) & {} (shadow)", + debug_info.num_chunks, debug_info.num_visible_chunks, debug_info.num_shadow_chunks, )) .color(TEXT_COLOR) .down_from(self.ids.entity_count, 5.0) @@ -1498,13 +1510,21 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_chunks, ui_widgets); + // Number of lights + Text::new(&format!("Lights: {}", debug_info.num_lights,)) + .color(TEXT_COLOR) + .down_from(self.ids.num_chunks, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.num_lights, ui_widgets); + // Number of figures Text::new(&format!( "Figures: {} ({} visible)", debug_info.num_figures, debug_info.num_figures_visible, )) .color(TEXT_COLOR) - .down_from(self.ids.num_chunks, 5.0) + .down_from(self.ids.num_lights, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_figures, ui_widgets); @@ -1867,6 +1887,9 @@ impl Hud { settings_window::Event::AdjustViewDistance(view_distance) => { events.push(Event::AdjustViewDistance(view_distance)); }, + settings_window::Event::AdjustLodDetail(lod_detail) => { + events.push(Event::AdjustLodDetail(lod_detail)); + }, settings_window::Event::AdjustSpriteRenderDistance(view_distance) => { events.push(Event::AdjustSpriteRenderDistance(view_distance)); }, @@ -1909,14 +1932,8 @@ 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::ChangeRenderMode(new_render_mode) => { + events.push(Event::ChangeRenderMode(new_render_mode)); }, settings_window::Event::ChangeResolution(new_resolution) => { events.push(Event::ChangeResolution(new_resolution)); @@ -2142,10 +2159,10 @@ impl Hud { events.push(Event::SwapSlots(a, b)); } else if let (Inventory(i), Hotbar(h)) = (a, b) { self.hotbar.add_inventory_link(h, i.0); - events.push(Event::ChangeHotbarState(self.hotbar.to_owned())); + events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); } else if let (Hotbar(a), Hotbar(b)) = (a, b) { self.hotbar.swap(a, b); - events.push(Event::ChangeHotbarState(self.hotbar.to_owned())); + events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); } }, slot::Event::Dropped(from) => { @@ -2154,7 +2171,7 @@ impl Hud { events.push(Event::DropSlot(from)); } else if let Hotbar(h) = from { self.hotbar.clear_slot(h); - events.push(Event::ChangeHotbarState(self.hotbar.to_owned())); + events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); } }, slot::Event::Used(from) => { @@ -2452,7 +2469,7 @@ impl Hud { &mut self, client: &Client, global_state: &mut GlobalState, - debug_info: DebugInfo, + debug_info: &Option, camera: &Camera, dt: Duration, info: HudInfo, @@ -2461,8 +2478,7 @@ impl Hud { if self.ui.ui.global_input().events().any(|event| { use conrod_core::{event, input}; matches!(event, - //event::Event::Raw(event::Input::Press(input::Button::Keyboard(input::Key::Tab))) - // => true, + /* event::Event::Raw(event::Input::Press(input::Button::Keyboard(input::Key::Tab))) | */ event::Event::Ui(event::Ui::Press( _, event::Press { @@ -2476,6 +2492,11 @@ impl Hud { .handle_event(conrod_core::event::Input::Text("\t".to_string())); } + if !self.show.ui { + // Optimization: skip maintaining UI when it's off. + return vec![]; + } + if let Some(maybe_id) = self.to_focus.take() { self.ui.focus_widget(maybe_id); } @@ -2483,14 +2504,16 @@ impl Hud { let camera::Dependents { view_mat, proj_mat, .. } = camera.dependents(); - self.ui.maintain( - &mut global_state.window.renderer_mut(), - Some(proj_mat * view_mat), - ); + let focus_off = camera.get_focus_pos().map(f32::trunc); // Check if item images need to be reloaded self.item_imgs.reload_if_changed(&mut self.ui); + self.ui.maintain( + &mut global_state.window.renderer_mut(), + Some(proj_mat * view_mat * Mat4::translation_3d(-focus_off)), + ); + events } diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 3a990f5220..78d6161f79 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -109,17 +109,23 @@ impl<'a> Ingameable for Overhead<'a> { // Number of conrod primitives contained in the overhead display. TODO maybe // this could be done automatically? // - 2 Text::new for name - // If HP Info is shown + 6 + // + // If HP Info is shown: // - 1 for level: either Text or Image - // - 4 for HP + mana + fg + bg - // - 1 for HP Text - // If there's a speech bubble + 13 - // - 2 Text::new for speec8 bubble + // - 3 for HP + fg + bg + // - 1 for HP text + // - If there's mana + // - 1 Rect::new for mana + // + // If there's a speech bubble + // - 2 Text::new for speech bubble // - 1 Image::new for icon // - 10 Image::new for speech bubble (9-slice + tail) 2 + if self.bubble.is_some() { 13 } else { 0 } - + if (self.stats.health.current() as f64 / self.stats.health.maximum() as f64) < 1.0 { - 6 + + if f64::from(self.stats.health.current()) / f64::from(self.stats.health.maximum()) + < 1.0 + { + 5 + if self.energy.is_some() { 1 } else { 0 } } else { 0 } @@ -291,10 +297,11 @@ impl<'a> Widget for Overhead<'a> { .mid_bottom_with_margin_on(state.ids.speech_bubble_text, -32.0); if dark_mode { - tail.w_h(22.0, 13.0).set(state.ids.speech_bubble_tail, ui) + tail.w_h(22.0, 13.0) } else { - tail.w_h(22.0, 28.0).set(state.ids.speech_bubble_tail, ui) - }; + tail.w_h(22.0, 28.0) + } + .set(state.ids.speech_bubble_tail, ui); let mut text_shadow = Text::new(&bubble_contents) .color(shadow_color) @@ -320,6 +327,8 @@ impl<'a> Widget for Overhead<'a> { Image::new(icon) .w_h(16.0, 16.0) .top_left_with_margin_on(state.ids.speech_bubble_text, -16.0) + // TODO: Figure out whether this should be parented. + // .parent(id) .set(state.ids.speech_bubble_icon, ui); } diff --git a/voxygen/src/hud/overitem.rs b/voxygen/src/hud/overitem.rs index bb4458ecb6..46da9859db 100644 --- a/voxygen/src/hud/overitem.rs +++ b/voxygen/src/hud/overitem.rs @@ -17,7 +17,7 @@ widget_ids! { #[derive(WidgetCommon)] pub struct Overitem<'a> { name: &'a str, - distance: &'a f32, + _distance: &'a f32, fonts: &'a ConrodVoxygenFonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -27,7 +27,7 @@ impl<'a> Overitem<'a> { pub fn new(name: &'a str, distance: &'a f32, fonts: &'a ConrodVoxygenFonts) -> Self { Self { name, - distance, + _distance: distance, fonts, common: widget::CommonBuilder::default(), } @@ -63,8 +63,8 @@ impl<'a> Widget for Overitem<'a> { fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { state, ui, .. } = args; - let font_size = - ((1.0 - (self.distance / common::comp::MAX_PICKUP_RANGE_SQR)) * 30.0) as u32; + let font_size = 30; + // ((1.0 - (self.distance / common::comp::MAX_PICKUP_RANGE_SQR)) * 30.0) as u32; // ItemName Text::new(&self.name) diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 7ece673e69..3d7d53d47a 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}, + render::{AaMode, CloudMode, FluidMode, LightingMode, RenderMode, ShadowMapMode, ShadowMode}, ui::{fonts::ConrodVoxygenFonts, ImageSlider, ScaleMode, ToggleButton}, window::GameInput, GlobalState, @@ -16,6 +16,7 @@ use conrod_core::{ widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use core::convert::TryFrom; use itertools::Itertools; use std::iter::once; @@ -94,6 +95,9 @@ widget_ids! { vd_slider, vd_text, vd_value, + lod_detail_slider, + lod_detail_text, + lod_detail_value, sprite_dist_slider, sprite_dist_text, sprite_dist_value, @@ -128,6 +132,13 @@ widget_ids! { // fullscreen_button, fullscreen_label, + lighting_mode_text, + lighting_mode_list, + shadow_mode_text, + shadow_mode_list, + shadow_mode_map_resolution_text, + shadow_mode_map_resolution_slider, + shadow_mode_map_resolution_value, save_window_size_button, audio_volume_slider, audio_volume_text, @@ -245,13 +256,12 @@ pub enum Event { AdjustSpriteRenderDistance(u32), AdjustFigureLoDRenderDistance(u32), AdjustFOV(u16), + AdjustLodDetail(u32), AdjustGamma(f32), AdjustWindowSize([u16; 2]), ToggleParticlesEnabled(bool), ToggleFullscreen, - ChangeAaMode(AaMode), - ChangeCloudMode(CloudMode), - ChangeFluidMode(FluidMode), + ChangeRenderMode(Box), ChangeResolution([u16; 2]), ChangeBitDepth(Option), ChangeRefreshRate(Option), @@ -269,7 +279,7 @@ pub enum Event { SctDamageBatch(bool), SpeechBubbleDarkMode(bool), SpeechBubbleIcon(bool), - ChangeLanguage(LanguageMetadata), + ChangeLanguage(Box), ChangeBinding(GameInput), ResetBindings, ChangeFreeLookBehavior(PressBehavior), @@ -1239,7 +1249,9 @@ impl<'a> Widget for SettingsWindow<'a> { .label_font_id(self.fonts.cyri.conrod_id) .set(state.ids.languages_list, ui) { - events.push(Event::ChangeLanguage(language_list[clicked].to_owned())); + events.push(Event::ChangeLanguage(Box::new( + language_list[clicked].to_owned(), + ))); } } @@ -1714,7 +1726,10 @@ impl<'a> Widget for SettingsWindow<'a> { if let Some(new_val) = ImageSlider::discrete( self.global_state.settings.graphics.view_distance, 1, - 65, + // FIXME: Move back to 64 once we support multiple texture atlases, or figure out a + // way to increase the size of the terrain atlas. + 25, + // 65, self.imgs.slider_indicator, self.imgs.slider, ) @@ -1805,9 +1820,47 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.fov_value, ui); + // LoD detail + Text::new(&self.localized_strings.get("hud.settings.lod_detail")) + .down_from(state.ids.fov_slider, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.lod_detail_text, ui); + + if let Some(new_val) = ImageSlider::discrete( + ((self.global_state.settings.graphics.lod_detail as f32 / 100.0).log(5.0) * 10.0) + .round() as i32, + 0, + 20, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .down_from(state.ids.lod_detail_text, 8.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.lod_detail_slider, ui) + { + events.push(Event::AdjustLodDetail( + (5.0f32.powf(new_val as f32 / 10.0) * 100.0) as u32, + )); + } + + Text::new(&format!( + "{}", + self.global_state.settings.graphics.lod_detail + )) + .right_from(state.ids.lod_detail_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.lod_detail_value, ui); + // Gamma Text::new(&self.localized_strings.get("hud.settings.gamma")) - .down_from(state.ids.fov_slider, 10.0) + .down_from(state.ids.lod_detail_slider, 10.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) @@ -1917,6 +1970,8 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.figure_dist_value, ui); + let render_mode = &self.global_state.settings.graphics.render_mode; + // AaMode Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode")) .down_from(state.ids.gamma_slider, 8.0) @@ -1925,27 +1980,26 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.aa_mode_text, ui); + // NOTE: MSAA modes are currently disabled from the UI due to poor + // interaction with greedy meshing, and may eventually be removed. let mode_list = [ AaMode::None, AaMode::Fxaa, - AaMode::MsaaX4, + /* AaMode::MsaaX4, AaMode::MsaaX8, - AaMode::MsaaX16, + AaMode::MsaaX16, */ AaMode::SsaaX4, ]; let mode_label_list = [ - "No AA", - "FXAA", - "MSAA x4", + "No AA", "FXAA", + /* "MSAA x4", "MSAA x8", - "MSAA x16 (experimental)", + "MSAA x16 (experimental)", */ "SSAA x4", ]; // Get which AA mode is currently active - let selected = mode_list - .iter() - .position(|x| *x == self.global_state.settings.graphics.aa_mode); + let selected = mode_list.iter().position(|x| *x == render_mode.aa); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1955,7 +2009,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(Box::new(RenderMode { + aa: mode_list[clicked], + ..render_mode.clone() + }))); } // CloudMode @@ -1979,9 +2036,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); + let selected = mode_list.iter().position(|x| *x == render_mode.cloud); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -1991,7 +2046,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(Box::new(RenderMode { + cloud: mode_list[clicked], + ..render_mode.clone() + }))); } // FluidMode @@ -2006,7 +2064,11 @@ impl<'a> Widget for SettingsWindow<'a> { .color(TEXT_COLOR) .set(state.ids.fluid_mode_text, ui); - let mode_list = [FluidMode::Cheap, FluidMode::Shiny]; + // FIXME: Add shiny water back to the UI once we fix the bug on nVidia cards. + let mode_list = [ + FluidMode::Cheap, + // FluidMode::Shiny + ]; let mode_label_list = [ &self .localized_strings @@ -2017,9 +2079,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); + let selected = mode_list.iter().position(|x| *x == render_mode.fluid); if let Some(clicked) = DropDownList::new(&mode_label_list, selected) .w_h(400.0, 22.0) @@ -2029,14 +2089,155 @@ 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(Box::new(RenderMode { + fluid: mode_list[clicked], + ..render_mode.clone() + }))); + } + + // LightingMode + Text::new( + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode"), + ) + .down_from(state.ids.fluid_mode_list, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.lighting_mode_text, ui); + + let mode_list = [ + LightingMode::Ashikhmin, + LightingMode::BlinnPhong, + LightingMode::Lambertian, + ]; + let mode_label_list = [ + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode.ashikhmin"), + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode.blinnphong"), + &self + .localized_strings + .get("hud.settings.lighting_rendering_mode.lambertian"), + ]; + + // Get which lighting rendering mode is currently active + let selected = mode_list.iter().position(|x| *x == render_mode.lighting); + + 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.lighting_mode_text, 8.0) + .set(state.ids.lighting_mode_list, ui) + { + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + lighting: mode_list[clicked], + ..render_mode.clone() + }))); + } + + // 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 shadow_map_mode = ShadowMapMode::try_from(render_mode.shadow).ok(); + let mode_list = [ + ShadowMode::None, + ShadowMode::Cheap, + ShadowMode::Map(shadow_map_mode.unwrap_or_default()), + ]; + 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 == 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(Box::new(RenderMode { + shadow: mode_list[clicked], + ..render_mode.clone() + }))); + } + + if let Some(shadow_map_mode) = shadow_map_mode { + // Display the shadow map mode if selected. + Text::new( + &self + .localized_strings + .get("hud.settings.shadow_rendering_mode.map.resolution"), + ) + .right_from(state.ids.shadow_mode_list, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.shadow_mode_map_resolution_text, ui); + + if let Some(new_val) = ImageSlider::discrete( + (shadow_map_mode.resolution.log2() * 4.0).round() as i8, + -8, + 8, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .right_from(state.ids.shadow_mode_map_resolution_text, 8.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.shadow_mode_map_resolution_slider, ui) + { + events.push(Event::ChangeRenderMode(Box::new(RenderMode { + shadow: ShadowMode::Map(ShadowMapMode { + resolution: 2.0f32.powf(f32::from(new_val) / 4.0), + }), + ..render_mode.clone() + }))); + } + + // TODO: Consider fixing to avoid allocation (it's probably not a bottleneck but + // there's no reason to allocate for numbers). + Text::new(&format!("{}", shadow_map_mode.resolution)) + .right_from(state.ids.shadow_mode_map_resolution_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.shadow_mode_map_resolution_value, ui); } // Particles Text::new(&self.localized_strings.get("hud.settings.particles")) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .down_from(state.ids.fluid_mode_list, 8.0) + .down_from(state.ids.shadow_mode_list, 8.0) .color(TEXT_COLOR) .set(state.ids.particles_label, ui); diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 9eeeb17318..ff26ee3f13 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] -#![allow(clippy::option_map_unit_fn)] -#![feature(drain_filter, bool_to_option, or_patterns)] +#![allow(clippy::option_map_unit_fn, incomplete_features)] +#![feature(array_map, bool_to_option, const_generics, drain_filter, or_patterns)] #![recursion_limit = "2048"] #[macro_use] diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index ece5cb3ea4..2edbc0f330 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -25,13 +25,15 @@ pub struct CharSelectionState { impl CharSelectionState { /// Create a new `CharSelectionState`. pub fn new(global_state: &mut GlobalState, client: Rc>) -> Self { + let scene = Scene::new( + global_state.window.renderer_mut(), + Some("fixture.selection_bg"), + &*client.borrow(), + ); Self { char_selection_ui: CharSelectionUi::new(global_state), client, - scene: Scene::new( - global_state.window.renderer_mut(), - Some("fixture.selection_bg"), - ), + scene, } } diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index d14a4e3549..dc89969460 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -204,9 +204,10 @@ impl<'a> MainMenuUi { // Load images let imgs = Imgs::load(&mut ui).expect("Failed to load images"); let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load images!"); - let bg_img_id = ui.add_graphic(Graphic::Image(load_expect( - bg_imgs.choose(&mut rng).unwrap(), - ))); + let bg_img_id = ui.add_graphic(Graphic::Image( + load_expect(bg_imgs.choose(&mut rng).unwrap()), + None, + )); //let chosen_tip = *tips.choose(&mut rng).unwrap(); // Load language let voxygen_i18n = load_expect::(&i18n_asset_key( diff --git a/voxygen/src/mesh/greedy.rs b/voxygen/src/mesh/greedy.rs new file mode 100644 index 0000000000..d906ea4d24 --- /dev/null +++ b/voxygen/src/mesh/greedy.rs @@ -0,0 +1,636 @@ +use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline}; +use vek::*; + +type TerrainVertex = ::Vertex; + +type TodoRect = ( + Vec3, + Vec2>, + guillotiere::Rectangle, + Vec3, +); + +pub struct GreedyConfig { + pub data: D, + /// The minimum position to mesh, in the coordinate system used + /// for queries against the volume. + pub draw_delta: Vec3, + /// 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`. + pub greedy_size: Vec3, + /// 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 around 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. + pub greedy_size_cross: Vec3, + /// Given a position, return the lighting information for the voxel at that + /// position. + pub get_light: FL, + /// Given a position, return the color information for the voxel at that + /// position. + pub get_color: FC, + /// 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). + pub get_opacity: FO, + /// 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. + pub should_draw: FS, + /// Create an opauqe quad (used for only display rendering) from its + /// top-left atlas position, the rectangle's dimensions in (2D) atlas + /// space, a world position, the u and v axes of the rectangle in (3D) + /// world space, the normal facing out frmo the rectangle in world + /// space, and meta information common to every voxel in this rectangle. + pub push_quad: FP, +} + +/// 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> { + /// Construct a new greedy mesher. + /// + /// Takes as input 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. + 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(), + } + } + + /// Perform greedy meshing on a model, separately producing "pure" model + /// data (the opaque mesh, together with atlas positions connecting + /// each rectangle with texture information), and raw light and color + /// data ready to be used as a texture (accessible with `finalize`). + /// Texture data built up within the same greedy mesh will be inserted + /// into the same atlas, which can be used to group texture data for + /// things like figures that are the result of meshing multiple models. + /// + /// Returns an estimate of the bounds of the current meshed model. + /// + /// For more information on the config parameter, see [GreedyConfig]. + pub fn push( + &mut self, + config: GreedyConfig, + ) where + FL: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, + FC: for<'r> FnMut(&'r mut D, Vec3) -> Rgb + 'a, + FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, + FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, + FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), + { + let cont = greedy_mesh( + &mut self.atlas, + &mut self.col_lights_size, + self.max_size, + config, + ); + self.suspended.push(cont); + } + + /// Finalize the mesh, producing texture color data for the whole model. + /// + /// By delaying finalization until the contents of the whole texture atlas + /// are known, we can perform just a single allocation to construct a + /// precisely fitting atlas. This will also let us (in the future) + /// suspend meshing partway through in order to meet frame budget, and + /// potentially use a single staged upload to the GPU. + /// + /// Returns the ColLightsInfo corresponding to the consstructed atlas. + pub fn finalize(self) -> ColLightInfo { + let cur_size = self.col_lights_size; + let col_lights = vec![ + 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 } +} + +fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FC, FO, FS, FP>( + atlas: &mut guillotiere::SimpleAtlasAllocator, + col_lights_size: &mut Vec2, + max_size: guillotiere::Size, + GreedyConfig { + mut data, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + mut should_draw, + mut push_quad, + }: GreedyConfig, +) -> Box> +where + FL: for<'r> FnMut(&'r mut D, Vec3) -> f32 + 'a, + FC: for<'r> FnMut(&'r mut D, Vec3) -> Rgb + 'a, + FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, + FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, + FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), +{ + // TODO: Collect information to see if we can choose a good value here. + let mut todo_rects = Vec::with_capacity(1024); + + // 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(), + 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(); + 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( + pos, + dim, + uv, + norm, + faces_forward, + meta, + atlas_pos, + |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(); + 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( + pos, + dim, + uv, + norm, + faces_forward, + meta, + atlas_pos, + |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| { + 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(); + 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( + pos, + dim, + uv, + norm, + faces_forward, + meta, + atlas_pos, + |atlas_pos, dim, pos, draw_dim, norm, meta| { + push_quad(atlas_pos, dim, pos, draw_dim, norm, meta) + }, + ); + }, + ); + + 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. +// TODO: See if we can speed a lot of this up using SIMD. +fn greedy_mesh_cross_section( + 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), 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, + 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 + ); + } + // 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); + } + // 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), + ); + + // 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. +// +// TODO: See if we can speed this up using SIMD. +fn draw_col_lights( + (col_lights, cur_size): &mut ColLightInfo, + data: &mut D, + todo_rects: Vec, + 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, +) { + todo_rects.into_iter().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; + let height = (rect.max.y - rect.min.y) as u16; + 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; + (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 + delta; + + // 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. + // 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); + + // 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). + get_light(data, light_pos) + + get_light(data, light_pos - uv.x) + + get_light(data, light_pos - uv.y) + + if direct_u_opacity || direct_v_opacity { + get_light(data, light_pos - uv.x - uv.y) + } else { + 0.0 + } + ) / 4.0; + let col = get_color(data, pos); + 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). +// TODO: See if we can speed a lot of this up using SIMD. +fn create_quad_greedy( + origin: Vec3, + dim: Vec2, + uv: Vec2>, + norm: Vec3, + faces_forward: bool, + meta: &M, + atlas_pos: guillotiere::Rectangle, + mut push_quad: impl FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), +) { + let origin = origin.map(|e| e as f32); + // 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 { + (draw_dim, dim, norm) + } else { + ( + Vec2::new(draw_dim.y, draw_dim.x), + Vec2::new(dim.y, dim.x), + -norm, + ) + }; + let norm = norm.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); + 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), + ) +} diff --git a/voxygen/src/mesh/mod.rs b/voxygen/src/mesh/mod.rs index 695c1e617b..657d635e32 100644 --- a/voxygen/src/mesh/mod.rs +++ b/voxygen/src/mesh/mod.rs @@ -1,17 +1,26 @@ +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 type MeshGen = ( + Mesh<>::Pipeline>, + Mesh<>::TranslucentPipeline>, + Mesh<>::ShadowPipeline>, + >::Result, +); + +/// FIXME: Remove this whole trait at some point. This "abstraction" is never +/// abstracted over, and is organized completely differently from how we +/// actually mesh things nowadays. +pub trait Meshable { type Pipeline: render::Pipeline; type TranslucentPipeline: render::Pipeline; + type ShadowPipeline: render::Pipeline; type Supplement; + type Result; - // Generate meshes - one opaque, one translucent - fn generate_mesh( - &'a self, - supp: Self::Supplement, - ) -> (Mesh, Mesh); + // Generate meshes - one opaque, one translucent, one shadow + fn generate_mesh(self, supp: Self::Supplement) -> MeshGen; } diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index 2909c51f35..62ff06078b 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -1,87 +1,146 @@ use crate::{ - mesh::{vol, Meshable}, - render::{self, FigurePipeline, Mesh, ParticlePipeline, SpritePipeline}, + mesh::{ + greedy::{self, GreedyConfig, GreedyMesh}, + MeshGen, Meshable, + }, + render::{ + self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline, + TerrainPipeline, + }, + scene::math, }; use common::{ figure::Cell, - util::{linear_to_srgb, srgb_to_linear}, vol::{BaseVol, ReadVol, SizedVol, Vox}, }; +use core::ops::Range; use vek::*; -type FigureVertex = ::Vertex; type SpriteVertex = ::Vertex; +type TerrainVertex = ::Vertex; type ParticleVertex = ::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 Supplement = (Vec3, Vec3); + type Pipeline = TerrainPipeline; + /// NOTE: The result provides the (roughly) computed bounds for the model, + /// and the vertex range meshed for this model; we return this instead + /// of the full opaque mesh so we can avoid allocating a separate mesh + /// for each bone. + /// + /// Later, we can iterate through the bone array and correctly assign bone + /// ids to all vertices in range for each segment. + /// + /// FIXME: A refactor of the figure cache to not just return an array of + /// models (thus allowing us to knoe the bone index ahead of time) would + /// avoid needing per-bone information at all. + type Result = (math::Aabb, Range); + type ShadowPipeline = ShadowPipeline; + type Supplement = ( + &'b mut GreedyMesh<'a>, + &'b mut Mesh, + Vec3, + Vec3, + ); type TranslucentPipeline = FigurePipeline; - #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, - ) -> (Mesh, Mesh) { - let mut mesh = Mesh::new(); + self, + (greedy, opaque_mesh, offs, scale): Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 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 lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + // NOTE: Figure sizes should be no more than 512 along each axis. + let greedy_size = upper_bound - lower_bound + 1; + assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); + let greedy_size_cross = greedy_size; + let draw_delta = lower_bound; - 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| { - 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| { + TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, 0) + }; - (mesh, Mesh::new()) + let start = opaque_mesh.vertices().len(); + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), + )); + }, + }); + let bounds = math::Aabb { + // NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16. + min: math::Vec3::from((lower_bound.as_::() + offs) * scale), + max: math::Vec3::from((upper_bound.as_::() + offs) * scale), + } + .made_valid(); + let vertex_range = start..opaque_mesh.vertices().len(); + + ( + Mesh::new(), + Mesh::new(), + Mesh::new(), + (bounds, vertex_range), + ) } } -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 @@ -89,66 +148,93 @@ where * &'a V: BaseVol, */ { type Pipeline = SpritePipeline; - type Supplement = (Vec3, Vec3); + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh, bool); type TranslucentPipeline = SpritePipeline; - #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, - ) -> (Mesh, Mesh) { - let mut mesh = Mesh::new(); + self, + (greedy, opaque_mesh, vertical_stripes): Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 16); - let 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 lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + let greedy_size = upper_bound - lower_bound + 1; + assert!( + greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, + "Sprite size out of bounds: {:?} ≤ (15, 15, 63)", + greedy_size - 1 + ); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); - 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| { - 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 greedy_size_cross = greedy_size; + let draw_delta = lower_bound; + + 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()) + }) + }; + let create_opaque = + |atlas_pos, pos: Vec3, norm, _meta| SpriteVertex::new(atlas_pos, pos, norm); - (mesh, Mesh::new()) + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), + )); + }, + }); + + (Mesh::new(), Mesh::new(), Mesh::new(), ()) } } -impl<'a, V: 'a> Meshable<'a, ParticlePipeline, ParticlePipeline> for V +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, /* TODO: Use VolIterator instead of manually iterating @@ -156,85 +242,127 @@ where * &'a V: BaseVol, */ { type Pipeline = ParticlePipeline; - type Supplement = (Vec3, Vec3); + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = &'b mut GreedyMesh<'a>; type TranslucentPipeline = ParticlePipeline; - #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - (offs, scale): Self::Supplement, - ) -> (Mesh, Mesh) { - let mut mesh = Mesh::new(); + self, + greedy: Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 16); - let 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 lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + let greedy_size = upper_bound - lower_bound + 1; + assert!( + greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, + "Particle size out of bounds: {:?} ≤ (15, 15, 63)", + greedy_size - 1 + ); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); - 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| { - ParticleVertex::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 greedy_size_cross = greedy_size; + let draw_delta = lower_bound; + + 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: Vec3, norm| ParticleVertex::new(pos, norm); - (mesh, Mesh::new()) + let mut opaque_mesh = Mesh::new(); + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), + )); + }, + }); + + (opaque_mesh, Mesh::new(), Mesh::new(), ()) } } -/// 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( pos: Vec3, - error_makes_face: bool, - should_add: impl Fn(&V::Vox) -> bool, -) -> [bool; 6] { - let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); - let make_face = |offset| { - seg.get(pos + offset) - .map(|v| should_add(v)) - .unwrap_or(error_makes_face) - }; - [ - 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 */ ())> { + let from = flat_get(pos - delta); + let to = flat_get(pos); + 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, ())) + } +} + +fn should_draw_greedy_ao( + vertical_stripes: bool, + pos: Vec3, + delta: Vec3, + _uv: Vec2>, + flat_get: impl Fn(Vec3) -> Cell, +) -> Option<(bool, bool)> { + let from = flat_get(pos - delta); + let to = flat_get(pos); + let from_opaque = !from.is_empty(); + if from_opaque != to.is_empty() { + None + } else { + let faces_forward = from_opaque; + let ao = !vertical_stripes || (pos.z & 1) != 0; + // If going from transparent to opaque, backward facing; otherwise, forward + // facing. + Some((faces_forward, ao)) + } } diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index b465565b71..4a7c008550 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -1,10 +1,13 @@ use crate::{ - mesh::{vol, Meshable}, - render::{self, FluidPipeline, Mesh, TerrainPipeline}, + mesh::{ + greedy::{self, GreedyConfig, GreedyMesh}, + MeshGen, Meshable, + }, + render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline}, }; use common::{ terrain::{Block, BlockKind}, - vol::{DefaultVolIterator, ReadVol, RectRasterableVol, Vox}, + vol::{ReadVol, RectRasterableVol, Vox}, volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d}, }; use std::{collections::VecDeque, fmt::Debug}; @@ -13,6 +16,16 @@ use vek::*; type TerrainVertex = ::Vertex; type FluidVertex = ::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; } @@ -27,7 +40,7 @@ impl Blendable for BlockKind { } const SUNLIGHT: u8 = 24; -const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; +const _MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; fn calc_light + ReadVol + Debug>( bounds: Aabb, @@ -209,10 +222,12 @@ 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 Supplement = Aabb; + type Result = (Aabb, ColLightInfo); + type ShadowPipeline = ShadowPipeline; + type Supplement = (Aabb, Vec2); type TranslucentPipeline = FluidPipeline; #[allow(clippy::collapsible_if)] @@ -222,13 +237,14 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> #[allow(clippy::panic_params)] // TODO: Pending review in #587 fn generate_mesh( - &'a self, - range: Self::Supplement, - ) -> (Mesh, Mesh) { + self, + (range, max_texture_size): Self::Supplement, + ) -> MeshGen { // Find blocks that should glow - let lit_blocks = - DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) - .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); + // FIXME: Replace with real lit blocks when we actually have blocks that glow. + let lit_blocks = core::iter::empty(); + /* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) + .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */ // Calculate chunk lighting let mut light = calc_light(range, self, lit_blocks); @@ -278,7 +294,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), } } }; @@ -310,247 +326,121 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> } .min(range.size().d - 1); - // // We use multiple meshes and then combine them later such that we can group - // 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 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, range.size().h - 2, z_end - z_start + 1); + // NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5 + // + 14). FIXME: Make this function fallible, since the terrain + // information might be dynamically generated which would make this hard + // to enforce. + assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384); + // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, + // which always fits into a f32. + let max_bounds: Vec3 = greedy_size.as_::(); + // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, + // which always fits into a usize. + let greedy_size = greedy_size.as_::(); + let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z); + let draw_delta = Vec3::new(1, 1, z_start); + + let get_light = |_: &mut (), pos: Vec3| 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 flat_get = |pos| flat_get(pos); + let should_draw = |_: &mut (), pos: Vec3, delta: Vec3, _uv| { + should_draw_greedy(pos, delta, flat_get) + }; + // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. + let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32); + let create_opaque = |atlas_pos, pos, norm, meta| { + TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta) + }; + let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm); + + let mut greedy = GreedyMesh::new(max_size); let mut opaque_mesh = Mesh::new(); let mut fluid_mesh = Mesh::new(); + greedy.push(GreedyConfig { + data: (), + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_color, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta { + FaceKind::Opaque(meta) => { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), + )); + }, + FaceKind::Fluid => { + fluid_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + &(), + |atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm), + )); + }, + }, + }); - 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 { - for j in 0..3 { - for k in 0..3 { - blocks[k][j][i] = Some(flat_get( - Vec3::new(x, y, z_start) + Vec3::new(i as i32, j as i32, k as i32) - - 1, - )); - } - } - } + let min_bounds = mesh_delta; + let bounds = Aabb { + min: min_bounds, + max: max_bounds + min_bounds, + }; + let (col_lights, col_lights_size) = greedy.finalize(); - let mut lights = [[[None; 3]; 3]; 3]; - for i in 0..3 { - for j in 0..3 { - for k in 0..3 { - lights[k][j][i] = if blocks[k][j][i] - .map(|block| block.is_opaque()) - .unwrap_or(false) - { - None - } else { - Some(light( - Vec3::new( - x + range.min.x, - y + range.min.y, - z_start + range.min.z, - ) + Vec3::new(i as i32, j as i32, k as i32) - - 1, - )) - }; - } - } - } - - let get_color = |maybe_block: Option<&Block>, neighbour: bool| { - maybe_block - .filter(|vox| vox.is_opaque() && (!neighbour || vox.is_blended())) - .and_then(|vox| vox.get_color()) - .map(Rgba::from_opaque) - .unwrap_or(Rgba::zero()) - }; - - for z in z_start..z_end + 1 { - let pos = Vec3::new(x, y, z); - let offs = (pos - Vec3::new(1, 1, -range.min.z)).map(|e| e as f32); - - lights[0] = lights[1]; - lights[1] = lights[2]; - blocks[0] = blocks[1]; - blocks[1] = blocks[2]; - - for i in 0..3 { - for j in 0..3 { - let block = Some(flat_get(pos + Vec3::new(i as i32, j as i32, 2) - 1)); - blocks[2][j][i] = block; - } - } - for i in 0..3 { - for j in 0..3 { - lights[2][j][i] = if blocks[2][j][i] - .map(|block| block.is_opaque()) - .unwrap_or(false) - { - None - } else { - Some(light( - pos + range.min + Vec3::new(i as i32, j as i32, 2) - 1, - )) - }; - } - } - - let block = blocks[1][1][1]; - let colors = if block.map_or(false, |vox| vox.is_blended()) { - let mut colors = [[[Rgba::zero(); 3]; 3]; 3]; - for i in 0..3 { - for j in 0..3 { - for k in 0..3 { - colors[i][j][k] = get_color( - blocks[i][j][k].as_ref(), - i != 1 || j != 1 || k != 1, - ) - } - } - } - colors - } else { - [[[get_color(blocks[1][1][1].as_ref(), false); 3]; 3]; 3] - }; - - // let opaque_mesh_index = ((z - z_start) * opaque_meshes.len() as i32 / (z_end - // + 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()) { - vol::push_vox_verts( - &mut opaque_mesh, //selected_opaque_mesh, - faces_to_make(&blocks, false, |vox| !vox.is_opaque()), - offs, - &colors, - |pos, norm, col, light, ao| { - //let light = (light.min(ao) * 255.0) as u32; - let light = (light * 255.0) as u32; - let ao = (ao * 255.0) as u32; - let norm = 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 } - }; - TerrainVertex::new(norm, light, ao, pos, col) - }, - &lights, - ); - } else if block.map_or(false, |vox| vox.is_fluid()) { - vol::push_vox_verts( - &mut fluid_mesh, - faces_to_make(&blocks, false, |vox| vox.is_air()), - offs, - &colors, - |pos, norm, col, light, _ao| { - FluidVertex::new(pos, norm, col, light, 0.3) - }, - &lights, - ); - } - } - } - } - - // let opaque_mesh = opaque_meshes - // .into_iter() - // .rev() - // .fold(Mesh::new(), |mut opaque_mesh, m: Mesh| { - // m.verts().chunks_exact(3).rev().for_each(|vs| { - // opaque_mesh.push(vs[0]); - // opaque_mesh.push(vs[1]); - // opaque_mesh.push(vs[2]); - // }); - // opaque_mesh - // }); - - (opaque_mesh, fluid_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: bool, - should_add: impl Fn(Block) -> bool, -) -> [bool; 6] { - // Faces to draw - let make_face = |opt_v: Option| opt_v.map(|v| should_add(v)).unwrap_or(error_makes_face); - [ - 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]), - ] -} - -/* -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, - ); - } - } - } - } +fn should_draw_greedy( + pos: Vec3, + delta: Vec3, + flat_get: impl Fn(Vec3) -> Block, +) -> Option<(bool, FaceKind)> { + let from = flat_get(pos - delta); + let to = flat_get(pos); + 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 d0ff5af21a..0000000000 --- a/voxygen/src/mesh/vol.rs +++ /dev/null @@ -1,224 +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) -> P::Vertex>( - origin: Vec3, - unit_x: Vec3, - unit_y: Vec3, - norm: Vec3, - cols: Vec4>, - darkness_ao: Vec4<(f32, f32)>, - 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]), - vcons(origin, norm, cols[0], darkness[0], ao_map[0]), - vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1]), - vcons( - origin + unit_x + unit_y, - norm, - cols[2], - darkness[2], - ao_map[2], - ), - ) - } else { - Quad::new( - vcons(origin, norm, cols[0], darkness[0], ao_map[0]), - vcons(origin + unit_x, norm, cols[1], darkness[1], ao_map[1]), - vcons( - origin + unit_x + unit_y, - norm, - cols[2], - darkness[2], - ao_map[2], - ), - vcons(origin + unit_y, norm, cols[3], darkness[3], ao_map[3]), - ) - } -} - -pub fn push_vox_verts( - mesh: &mut Mesh

, - faces: [bool; 6], - offs: Vec3, - cols: &[[[Rgba; 3]; 3]; 3], - vcons: impl Fn(Vec3, Vec3, Rgb, f32, f32) -> P::Vertex, - darknesses: &[[[Option; 3]; 3]; 3], -) { - let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); - - // -x - if 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), - &vcons, - )); - } - // +x - if 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), - &vcons, - )); - } - // -y - if 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), - &vcons, - )); - } - // +y - if 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), - &vcons, - )); - } - // -z - if 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), - &vcons, - )); - } - // +z - if 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), - &vcons, - )); - } -} diff --git a/voxygen/src/render/consts.rs b/voxygen/src/render/consts.rs index effe76e8c0..0eded13428 100644 --- a/voxygen/src/render/consts.rs +++ b/voxygen/src/render/consts.rs @@ -23,9 +23,14 @@ impl Consts { &mut self, encoder: &mut gfx::Encoder, vals: &[T], + offset: usize, ) -> Result<(), RenderError> { - encoder - .update_buffer(&self.buf, vals, 0) - .map_err(RenderError::UpdateError) + if vals.is_empty() { + Ok(()) + } else { + encoder + .update_buffer(&self.buf, vals, offset) + .map_err(RenderError::UpdateError) + } } } diff --git a/voxygen/src/render/error.rs b/voxygen/src/render/error.rs index a0437c4794..8c9352857d 100644 --- a/voxygen/src/render/error.rs +++ b/voxygen/src/render/error.rs @@ -10,6 +10,7 @@ pub enum RenderError { IncludeError(glsl_include::Error), MappingError(gfx::mapping::Error), CopyError(gfx::CopyError<[u16; 3], usize>), + CustomError(String), } impl From> for RenderError { diff --git a/voxygen/src/render/instances.rs b/voxygen/src/render/instances.rs index c99be74ffa..c53d5ee2c2 100644 --- a/voxygen/src/render/instances.rs +++ b/voxygen/src/render/instances.rs @@ -15,7 +15,7 @@ impl Instances { pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result { Ok(Self { ibuf: factory - .create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::TRANSFER_DST) + .create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::empty()) .map_err(RenderError::BufferCreationError)?, }) } diff --git a/voxygen/src/render/mesh.rs b/voxygen/src/render/mesh.rs index 6b345505bb..4ddbe65843 100644 --- a/voxygen/src/render/mesh.rs +++ b/voxygen/src/render/mesh.rs @@ -1,4 +1,5 @@ use super::Pipeline; +use core::{iter::FromIterator, ops::Range}; /// A `Vec`-based mesh structure used to store mesh data on the CPU. pub struct Mesh { @@ -19,7 +20,7 @@ where impl Mesh

{ /// Create a new `Mesh`. #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { Self { verts: vec![] } } + pub fn new() -> Self { Self { verts: Vec::new() } } /// Clear vertices, allows reusing allocated memory of the underlying Vec. pub fn clear(&mut self) { self.verts.clear(); } @@ -68,6 +69,11 @@ impl Mesh

{ } pub fn iter(&self) -> std::slice::Iter { self.verts.iter() } + + /// NOTE: Panics if vertex_range is out of bounds of vertices. + pub fn iter_mut(&mut self, vertex_range: Range) -> std::slice::IterMut { + self.verts[vertex_range].iter_mut() + } } impl IntoIterator for Mesh

{ @@ -77,6 +83,24 @@ impl IntoIterator for Mesh

{ fn into_iter(self) -> Self::IntoIter { self.verts.into_iter() } } +impl FromIterator> for Mesh

{ + fn from_iter>>(tris: I) -> Self { + tris.into_iter().fold(Self::new(), |mut this, tri| { + this.push_tri(tri); + this + }) + } +} + +impl FromIterator> for Mesh

{ + fn from_iter>>(quads: I) -> Self { + quads.into_iter().fold(Self::new(), |mut this, quad| { + this.push_quad(quad); + this + }) + } +} + /// Represents a triangle stored on the CPU. pub struct Tri { a: P::Vertex, @@ -100,4 +124,18 @@ impl Quad

{ pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex, d: P::Vertex) -> Self { Self { a, b, c, d } } + + pub fn rotated_by(self, n: usize) -> Self + where + P::Vertex: Clone, + { + let verts = [self.a, self.b, self.c, self.d]; + + Self { + a: verts[n % 4].clone(), + b: verts[(1 + n) % 4].clone(), + c: verts[(2 + n) % 4].clone(), + d: verts[(3 + n) % 4].clone(), + } + } } diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 317ba97db6..fce129283f 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -16,24 +16,33 @@ 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, LodData, LodTerrainPipeline}, particle::{Instance as ParticleInstance, ParticlePipeline}, postprocess::{ create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline, }, + 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, Mode as UiMode, UiPipeline, }, - Globals, Light, Shadow, + GlobalModel, Globals, Light, Shadow, + }, + renderer::{ + ColLightFmt, ColLightInfo, LodAltFmt, LodColorFmt, LodTextureFmt, Renderer, + ShadowDepthStencilFmt, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt, }, - renderer::{Renderer, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt}, texture::Texture, }; +pub use gfx::texture::{FilterMethod, WrapMode}; #[cfg(feature = "gl")] use gfx_device_gl as gfx_backend; @@ -55,26 +64,201 @@ pub trait Pipeline { use serde_derive::{Deserialize, Serialize}; /// Anti-aliasing modes -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum AaMode { None, + /// Fast approximate antialiasing. + /// + /// This is a screen-space technique, and therefore works fine with greedy + /// meshing. Fxaa, + /// Multisampling AA, up to 4 samples per pixel. + /// + /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will + /// also struggle in the futrue with deferred shading, so they may be + /// removed in the future. MsaaX4, + /// Multisampling AA, up to 8 samples per pixel. + /// + /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will + /// also struggle in the futrue with deferred shading, so they may be + /// removed in the future. MsaaX8, + /// Multisampling AA, up to 16 samples per pixel. + /// + /// NOTE: MSAA modes don't (currently) work with greedy meshing, and will + /// also struggle in the futrue with deferred shading, so they may be + /// removed in the future. MsaaX16, + /// Super-sampling antialiasing, 4 samples per pixel. + /// + /// Unlike MSAA, SSAA *always* performs 4 samples per pixel, rather than + /// trying to choose importance samples at boundary regions, so it works + /// much better with techniques like deferred rendering and greedy + /// meshing that (without significantly more work) invalidate the + /// GPU's assumptions about importance sampling. SsaaX4, } +impl Default for AaMode { + fn default() -> Self { AaMode::Fxaa } +} + /// Cloud modes -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum CloudMode { + /// No clouds. On computers that can't handle loops well, have performance + /// issues in fragment shaders in general, or just have large + /// resolutions, this can be a *very* impactful performance difference. + /// Part of that is because of inefficiencies in how we implement + /// regular clouds. It is still not all that cheap on low-end machines, due + /// to many calculations being performed that use relatively expensive + /// functions, and at some point I'd like to both optimize the regular + /// sky shader further and create an even cheaper option. None, + /// Volumetric clouds. This option can be *very* expensive on low-end + /// machines, to the point of making the game unusable, for several + /// reasons: + /// + /// - The volumetric clouds use raymarching, which will cause catastrophic + /// performance degradation on GPUs without good support for loops. There + /// is an attempt to minimize the impact of this using a z-range check, + /// but on some low-end GPUs (such as some integraetd graphics cards) this + /// test doesn't appear to be able to be predicted well at shader + /// invocation time. + /// - The cloud computations themselves are fairly involved, further + /// degrading performance. + /// - Although the sky shader is always drawn at the outer edges of the + /// skybox, the clouds themselves are supposed to be positioned much + /// lower, which means the depth check for the skybox incorrectly cuts off + /// clouds in some places. To compensate for these cases (e.g. where + /// terrain is occluded by clouds from above, and the camera is above the + /// clouds), we currently branch to see if we need to render the clouds in + /// *every* fragment shader. For machines that can't optimize the check, + /// this is absurdly expensive, so we should look at alternatives in the + /// future that player better with the GPU. Regular, } +impl Default for CloudMode { + fn default() -> Self { CloudMode::Regular } +} + /// Fluid modes -#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum FluidMode { + /// "Cheap" water. This water implements no waves, no reflections, no + /// diffraction, and no light attenuation through water. As a result, + /// it can be much cheaper than shiny reflection. Cheap, + /// "Shiny" water. This water implements waves on the surfaces, some + /// attempt at reflections, and tries to compute accurate light + /// attenuation through water (this is what results in the + /// colors changing as you descend into deep water). + /// + /// Unfortunately, the way the engine is currently set up, calculating + /// accurate attenuation is a bit difficult; we use estimates from + /// horizon maps for the current water altitude, which can both be off + /// by up to (max_altitude / 255) meters, only has per-chunk horizontal + /// resolution, and cannot handle edge cases like horizontal water (e.g. + /// waterfalls) well. We are okay with the latter, and will try to fix + /// the former soon. + /// + /// Another issue is that we don't always know whether light is *blocked*, + /// which causes attenuation to be computed incorrectly; this can be + /// addressed by using shadow maps (at least for terrain). Shiny, } + +impl Default for FluidMode { + fn default() -> Self { FluidMode::Cheap } +} + +/// Lighting modes +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum LightingMode { + /// Ashikhmin-Shirley BRDF lighting model. Attempts to generate a + /// physically plausible (to some extent) lighting distribution. + /// + /// This mdoel may not work as well with purely directional lighting, and is + /// more expensive than the other models. + Ashikhmin, + /// Standard Blinn-Phong shading, combing Lambertian diffuse reflections and + /// specular highlights. + BlinnPhong, + /// Standard Lambertian lighting model, with only diffuse reflections. The + /// cheapest lighting model by a decent margin, but the performance + /// difference between it and Blinn-Phong will probably only be + /// significant on low-end machines that are bottlenecked on fragment + /// shading. + Lambertian, +} + +impl Default for LightingMode { + fn default() -> Self { LightingMode::BlinnPhong } +} + +/// Shadow map settings. +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub struct ShadowMapMode { + /// Multiple of default resolution (default, which is 1.0, is currently + /// the closest higher power of two above the length of the longest + /// diagonal of the screen resolution, but this may change). + pub resolution: f32, +} + +impl Default for ShadowMapMode { + fn default() -> Self { Self { resolution: 1.0 } } +} + +/// Shadow modes +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum ShadowMode { + /// No shadows at all. By far the cheapest option. + None, + /// Point shadows (draw circles under figures, up to a configured maximum; + /// also render LOD shadows using horizon maps). Can be expensive on + /// some machines, probably mostly due to horizon mapping; the point + /// shadows are not rendered too efficiently, but that can probably + /// be addressed later. + Cheap, + /// Shadow map (render the scene from each light source, and also renders + /// LOD shadows using horizon maps). + Map(ShadowMapMode), +} + +impl Default for ShadowMode { + fn default() -> Self { ShadowMode::Cheap } +} + +impl core::convert::TryFrom for ShadowMapMode { + type Error = (); + + /// Get the shadow map details if they exist. + fn try_from(value: ShadowMode) -> Result { + if let ShadowMode::Map(map) = value { + Ok(map) + } else { + Err(()) + } + } +} + +impl ShadowMode { + pub fn is_map(&self) -> bool { matches!(self, Self::Map(_)) } +} + +/// Render modes +#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)] +pub struct RenderMode { + #[serde(default)] + pub aa: AaMode, + #[serde(default)] + pub cloud: CloudMode, + #[serde(default)] + pub fluid: FluidMode, + #[serde(default)] + pub lighting: LightingMode, + #[serde(default)] + pub shadow: ShadowMode, +} diff --git a/voxygen/src/render/model.rs b/voxygen/src/render/model.rs index d994215d1d..119f314feb 100644 --- a/voxygen/src/render/model.rs +++ b/voxygen/src/render/model.rs @@ -22,6 +22,15 @@ impl Model

{ } pub fn vertex_range(&self) -> Range { self.vertex_range.clone() } + + /// Create a model with a slice of a portion of this model to send to the + /// renderer. + pub fn submodel(&self, vertex_range: Range) -> Model

{ + Model { + vbuf: self.vbuf.clone(), + vertex_range, + } + } } /// Represents a mesh on the GPU which can be updated dynamically. @@ -40,10 +49,10 @@ impl DynamicModel

{ /// Create a model with a slice of a portion of this model to send to the /// renderer. - pub fn submodel(&self, range: Range) -> Model

{ + pub fn submodel(&self, vertex_range: Range) -> Model

{ Model { vbuf: self.vbuf.clone(), - vertex_range: range.start as u32..range.end as u32, + vertex_range, } } diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index e0226d0aa0..03e071f667 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -1,36 +1,33 @@ use super::{ - super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, - Globals, Light, Shadow, + super::{Mesh, Model, Pipeline, TerrainPipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, }; +use crate::mesh::greedy::GreedyMesh; +use core::ops::Range; 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, state::ColorMask, }; use vek::*; gfx_defines! { - vertex Vertex { - pos_norm: u32 = "v_pos_norm", - col: u32 = "v_col", - // BBBBBBAA - // B = Bone - // A = AO - ao_bone: u8 = "v_ao_bone", - } - 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<::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,72 +35,91 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + 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))), - } -} - -impl Vertex { - #[allow(clippy::collapsible_if)] - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32, bone_idx: u8) -> Self { - 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: pos - .map2(Vec3::new(0, 9, 18), |e, shift| { - (((e * 2.0 + 256.0) as u32) & 0x3FF) << shift - }) - .reduce_bitor() - | (norm_bits << 29), - 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 with_bone_idx(mut self, bone_idx: u8) -> Self { - self.ao_bone = (self.ao_bone & 0b11) | (bone_idx << 2); - self + 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))), } } impl Locals { - pub fn new(model_mat: Mat4, col: Rgba, is_player: bool) -> Self { + pub fn new( + model_mat: anim::vek::Mat4, + col: Rgba, + pos: anim::vek::Vec3, + atlas_offs: Vec2, + is_player: bool, + ) -> Self { let mut flags = 0; flags |= is_player as u32; Self { model_mat: model_mat.into_col_arrays(), 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( + anim::vek::Mat4::identity(), + Rgba::broadcast(1.0), + anim::vek::Vec3::default(), + Vec2::default(), + false, + ) + } } impl BoneData { - pub fn new(bone_mat: Mat4) -> Self { + pub fn new(bone_mat: anim::vek::Mat4, normals_mat: anim::vek::Mat4) -> Self { Self { bone_mat: bone_mat.into_col_arrays(), + normals_mat: normals_mat.into_col_arrays(), } } +} - pub fn default() -> Self { Self::new(Mat4::identity()) } +impl Default for BoneData { + fn default() -> Self { Self::new(anim::vek::Mat4::identity(), anim::vek::Mat4::identity()) } } pub struct FigurePipeline; impl Pipeline for FigurePipeline { - type Vertex = Vertex; + type Vertex = ::Vertex; } + +pub struct FigureModel { + pub opaque: Model, + /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different + * LOD levels. */ +} + +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, (anim::vek::Aabb, Range)); diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs index 040a757a07..a871aa3c4a 100644 --- a/voxygen/src/render/pipelines/fluid.rs +++ b/voxygen/src/render/pipelines/fluid.rs @@ -1,19 +1,16 @@ 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", } pipeline pipe { @@ -24,18 +21,28 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + 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 { #[allow(clippy::identity_op)] // TODO: Pending review in #587 #[allow(clippy::into_iter_on_ref)] // TODO: Pending review in #587 - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, light: f32, _opac: f32) -> Self { + pub fn new(pos: Vec3, norm: Vec3) -> Self { let (norm_axis, norm_dir) = norm .as_slice() .into_iter() @@ -52,12 +59,6 @@ impl Vertex { | ((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 new file mode 100644 index 0000000000..9003525dc5 --- /dev/null +++ b/voxygen/src/render/pipelines/lod_terrain.rs @@ -0,0 +1,117 @@ +use super::{ + super::{ + LodAltFmt, LodColorFmt, LodTextureFmt, Pipeline, Renderer, Texture, TgtColorFmt, + TgtDepthStencilFmt, + }, + Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, texture::SamplerInfo, +}; +use vek::*; + +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + } + + constant Locals { + nul: [f32; 4] = "nul", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + map: gfx::TextureSampler<[f32; 4]> = "t_map", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + tgt_color: gfx::RenderTarget = "tgt_color", + 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(pos: Vec2) -> Self { + Self { + pos: pos.into_array(), + } + } +} + +impl Locals { + pub fn default() -> Self { Self { nul: [0.0; 4] } } +} + +pub struct LodTerrainPipeline; + +impl Pipeline for LodTerrainPipeline { + type Vertex = Vertex; +} + +pub struct LodData { + pub map: Texture, + pub alt: Texture, + pub horizon: Texture, + pub tgt_detail: u32, +} + +impl LodData { + pub fn new( + renderer: &mut Renderer, + map_size: Vec2, + lod_base: &[u32], + lod_alt: &[u32], + lod_horizon: &[u32], + tgt_detail: u32, + border_color: gfx::texture::PackedColor, + ) -> Self { + let kind = gfx::texture::Kind::D2(map_size.x, map_size.y, gfx::texture::AaMode::Single); + let info = gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Border, + ); + Self { + map: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_base)], + SamplerInfo { + border: border_color, + ..info + }, + ) + .expect("Failed to generate map texture"), + alt: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_alt)], + SamplerInfo { + border: [0.0, 0.0, 0.0, 0.0].into(), + ..info + }, + ) + .expect("Failed to generate alt texture"), + horizon: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_horizon)], + SamplerInfo { + border: [1.0, 0.0, 1.0, 0.0].into(), + ..info + }, + ) + .expect("Failed to generate horizon texture"), + tgt_detail, + } + } +} diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 442b31be32..db6c64ed8b 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1,30 +1,48 @@ pub mod figure; pub mod fluid; +pub mod lod_terrain; pub mod particle; pub mod postprocess; +pub mod shadow; pub mod skybox; pub mod sprite; pub mod terrain; pub mod ui; +use super::Consts; use crate::scene::camera::CameraMode; 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 = 31; +pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; +pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; + gfx_defines! { constant Globals { view_mat: [[f32; 4]; 4] = "view_mat", proj_mat: [[f32; 4]; 4] = "proj_mat", all_mat: [[f32; 4]; 4] = "all_mat", cam_pos: [f32; 4] = "cam_pos", + focus_off: [f32; 4] = "focus_off", focus_pos: [f32; 4] = "focus_pos", - // TODO: Fix whatever alignment issue requires these uniforms to be aligned. + /// NOTE: view_distance.x is the horizontal view distance, view_distance.y is the LOD + /// detail, view_distance.z is the + /// minimum height over any land chunk (i.e. the sea level), and view_distance.w is the + /// maximum height over this minimum height. + /// + /// TODO: Fix whatever alignment issue requires these uniforms to be aligned. view_distance: [f32; 4] = "view_distance", time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64. + sun_dir: [f32; 4] = "sun_dir", + moon_dir: [f32; 4] = "moon_dir", tick: [f32; 4] = "tick", + /// x, y represent the resolution of the screen; + /// w, z represent the near and far planes of the shadow map. screen_res: [f32; 4] = "screen_res", light_shadow_count: [u32; 4] = "light_shadow_count", + shadow_proj_factors: [f32; 4] = "shadow_proj_factors", medium: [u32; 4] = "medium", select_pos: [i32; 4] = "select_pos", gamma: [f32; 4] = "gamma", @@ -52,11 +70,15 @@ impl Globals { cam_pos: Vec3, focus_pos: Vec3, view_distance: f32, + tgt_detail: f32, + map_bounds: Vec2, time_of_day: f64, tick: f64, screen_res: Vec2, + shadow_planes: Vec2, light_count: usize, shadow_count: usize, + directed_light_count: usize, medium: BlockKind, select_pos: Option>, gamma: f32, @@ -68,12 +90,32 @@ impl Globals { proj_mat: proj_mat.into_col_arrays(), all_mat: (proj_mat * view_mat).into_col_arrays(), cam_pos: Vec4::from(cam_pos).into_array(), - focus_pos: Vec4::from(focus_pos).into_array(), - view_distance: [view_distance; 4], + 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], - screen_res: Vec4::from(screen_res.map(|e| e as f32)).into_array(), - light_shadow_count: [light_count as u32, shadow_count as u32, 0, 0], + // Provide the shadow map far plane as well. + screen_res: [ + screen_res.x as f32, + screen_res.y as f32, + shadow_planes.x, + shadow_planes.y, + ], + light_shadow_count: [ + (light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32, + (shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32, + (directed_light_count % (MAX_DIRECTED_LIGHT_COUNT + 1)) as u32, + 0, + ], + shadow_proj_factors: [ + (shadow_planes.y + shadow_planes.x) / (shadow_planes.y - shadow_planes.x), + (2.0 * shadow_planes.y * shadow_planes.x) / (shadow_planes.y - shadow_planes.x), + 0.0, + 0.0, + ], medium: [if medium.is_fluid() { 1 } else { 0 }; 4], select_pos: select_pos .map(|sp| Vec4::from(sp) + Vec4::unit_w()) @@ -84,6 +126,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).normalized() + } } impl Default for Globals { @@ -94,9 +151,13 @@ impl Default for Globals { Vec3::zero(), Vec3::zero(), 0.0, + 100.0, + Vec2::new(140.0, 2048.0), 0.0, 0.0, Vec2::new(800, 500), + Vec2::new(1.0, 25.0), + 0, 0, 0, BlockKind::Air, @@ -143,3 +204,11 @@ impl Shadow { impl Default for Shadow { fn default() -> Self { Self::new(Vec3::zero(), 0.0) } } + +// Global scene data spread across several arrays. +pub struct GlobalModel { + pub globals: Consts, + pub lights: Consts, + pub shadows: Consts, + pub shadow_mats: Consts, +} diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 616e3158c4..b2af4921f2 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -1,11 +1,10 @@ use super::{ super::{Pipeline, 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 vek::*; @@ -13,7 +12,7 @@ gfx_defines! { vertex Vertex { pos: [f32; 3] = "v_pos", // ____BBBBBBBBGGGGGGGGRRRRRRRR - col: u32 = "v_col", + // col: u32 = "v_col", // ...AANNN // A = AO // N = Normal @@ -54,16 +53,26 @@ gfx_defines! { lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + 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 Vertex { #[allow(clippy::collapsible_if)] - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32) -> Self { + pub fn new(pos: Vec3, norm: Vec3) -> Self { let norm_bits = if norm.x != 0.0 { if norm.x < 0.0 { 0 } else { 1 } } else if norm.y != 0.0 { @@ -74,10 +83,7 @@ impl Vertex { Self { 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), + norm_ao: norm_bits, } } } diff --git a/voxygen/src/render/pipelines/shadow.rs b/voxygen/src/render/pipelines/shadow.rs new file mode 100644 index 0000000000..cee7a2fd1e --- /dev/null +++ b/voxygen/src/render/pipelines/shadow.rs @@ -0,0 +1,90 @@ +use super::{ + super::{ + ColLightFmt, ColLightInfo, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt, + TerrainLocals, Texture, + }, + figure, terrain, Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, +}; +use vek::*; + +gfx_defines! { + constant Locals { + shadow_matrices: [[f32; 4]; 4] = "shadowMatrices", + texture_mats: [[f32; 4]; 4] = "texture_mat", + } + + pipeline pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + } + + pipeline figure_pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + bones: gfx::ConstantBuffer = "u_bones", + globals: gfx::ConstantBuffer = "u_globals", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + } +} + +impl Locals { + pub fn new(shadow_mat: Mat4, texture_mat: Mat4) -> Self { + Self { + shadow_matrices: shadow_mat.into_col_arrays(), + texture_mats: texture_mat.into_col_arrays(), + } + } + + 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], + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) + } +} + +impl Pipeline for ShadowPipeline { + type Vertex = terrain::Vertex; +} diff --git a/voxygen/src/render/pipelines/skybox.rs b/voxygen/src/render/pipelines/skybox.rs index e6cbd95649..9d2b43be3a 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! { @@ -23,10 +22,14 @@ gfx_defines! { locals: gfx::ConstantBuffer = "u_locals", globals: gfx::ConstantBuffer = "u_globals", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + 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::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 9eb562eb49..9ac8ed369f 100644 --- a/voxygen/src/render/pipelines/sprite.rs +++ b/voxygen/src/render/pipelines/sprite.rs @@ -1,52 +1,97 @@ use super::{ super::{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", + // 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_wind_sway: f32 = "inst_wind_sway", } pipeline pipe { vbuf: gfx::VertexBuffer = (), ibuf: gfx::InstanceBuffer = (), + 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", globals: gfx::ConstantBuffer = "u_globals", lights: gfx::ConstantBuffer = "u_lights", shadows: gfx::ConstantBuffer = "u_shadows", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + 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 & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF), + ) + .field("norm_ao", &self.norm_ao) + .finish() } } impl Vertex { + // NOTE: Limit to 16 (x) × 16 (y) × 32 (z). #[allow(clippy::collapsible_if)] - pub fn new(pos: Vec3, norm: Vec3, col: Rgb, ao: f32) -> Self { + 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 { @@ -56,31 +101,53 @@ impl Vertex { }; Self { + // pos_norm: ((pos.x as u32) & 0x003F) + // | ((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), + atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16, + norm_ao: norm_bits, } } } impl Instance { - pub fn new(mat: Mat4, col: Rgb, wind_sway: f32) -> Self { + pub fn new(mat: Mat4, wind_sway: f32, pos: Vec3, ori_bits: u8) -> Self { + const EXTRA_NEG_Z: i32 = 32768; + let mat_arr = mat.into_col_arrays(); Self { + pos_ori: ((pos.x as u32) & 0x003F) + | ((pos.y as u32) & 0x003F) << 6 + | (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12 + | (u32::from(ori_bits) & 0x7) << 29, inst_mat0: mat_arr[0], inst_mat1: mat_arr[1], inst_mat2: mat_arr[2], inst_mat3: mat_arr[3], - 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(), 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: mat.into_col_arrays(), + wind_sway: [scale.x, scale.y, scale.z, wind_sway], + offs: [offs.x, offs.y, offs.z, 0.0], + } + } } pub struct SpritePipeline; diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index e43cdc99ae..66eaaabf5d 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -1,60 +1,115 @@ 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", + 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 = (), + 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", + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + 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 { #[allow(clippy::identity_op)] // TODO: Pending review in #587 - pub fn new(norm_bits: u32, light: u32, ao: u32, pos: Vec3, col: Rgb) -> Self { - const EXTRA_NEG_Z: f32 = 65536.0; + /// NOTE: meta is true when the terrain vertex is touching water. + 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_norm: ((pos.x as u32) & 0x003F) << 0 | ((pos.y as u32) & 0x003F) << 6 - | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 17) as f32) as u32) & 0xFFFFF) << 12 + | (((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, - col_light: 0 - | ((col.r.mul(255.0) as u32) & 0xFF) << 8 - | ((col.g.mul(255.0) as u32) & 0xFF) << 16 - | ((col.b.mul(255.0) as u32) & 0xFF) << 24 - | (ao >> 6) << 6 - | ((light >> 2) & 0x3F) << 0, + atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) << 0 | ((atlas_pos.y as u32) & 0xFFFF) << 16, } } + + pub fn new_figure(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: ((atlas_pos.x as u32) & 0x7FFF) << 2 + | ((atlas_pos.y as u32) & 0x7FFF) << 17 + | axis_bits & 3, + } + } + + pub fn make_col_light( + light: u8, + col: Rgb, + ) -> <::Surface as gfx::format::SurfaceTyped>::DataType + { + [col.r, col.g, col.b, light] + } + + /// Set the bone_idx for an existing figure vertex. + pub fn set_bone_idx(&mut self, bone_idx: u8) { + self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27); + } } impl Locals { @@ -62,6 +117,7 @@ impl Locals { Self { model_offs: [0.0; 3], load_time: 0.0, + atlas_offs: [0; 4], } } } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 1fa5cc6f2c..41ba19f4ea 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -5,32 +5,38 @@ use super::{ mesh::Mesh, model::{DynamicModel, Model}, pipelines::{ - figure, fluid, particle, postprocess, skybox, sprite, terrain, ui, Globals, Light, Shadow, + figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, ui, + GlobalModel, Globals, }, texture::Texture, - AaMode, CloudMode, FluidMode, Pipeline, RenderError, + AaMode, CloudMode, FilterMethod, FluidMode, LightingMode, Pipeline, RenderError, RenderMode, + ShadowMapMode, 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; -use tracing::error; +use tracing::{error, warn}; use vek::*; /// Represents the format of the pre-processed color target. 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; /// Represents the format of the window's depth target. pub type WinDepthFmt = gfx::format::Depth; +/// Represents the format of the pre-processed shadow depth target. +pub type ShadowDepthStencilFmt = gfx::format::Depth; + /// A handle to a pre-processed color target. pub type TgtColorView = gfx::handle::RenderTargetView; /// A handle to a pre-processed depth target. @@ -42,12 +48,63 @@ pub type WinColorView = gfx::handle::RenderTargetView; +/// Represents the format of LOD shadows. +pub type LodTextureFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Unorm); + +/// Represents the format of LOD altitudes. +pub type LodAltFmt = (gfx::format::R16_G16, gfx::format::Unorm); + +/// Represents the format of LOD map colors. +pub type LodColorFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); + +/// 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; +/// A handle to a shadow depth target as a resource. +pub type ShadowResourceView = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; + /// A handle to a render color target as a resource. pub type TgtColorRes = gfx::handle::ShaderResourceView< gfx_backend::Resources, ::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 { + // directed_encoder: gfx::Encoder, + // point_encoder: gfx::Encoder, + directed_depth_stencil_view: ShadowDepthStencilView, + directed_res: ShadowResourceView, + directed_sampler: Sampler, + + point_depth_stencil_view: ShadowDepthStencilView, + point_res: ShadowResourceView, + point_sampler: Sampler, + + point_pipeline: GfxPipeline>, + terrain_directed_pipeline: GfxPipeline>, + figure_directed_pipeline: GfxPipeline>, +} + /// A type that encapsulates rendering state. `Renderer` is central to Voxygen's /// rendering subsystem and contains any state necessary to interact with the /// GPU, along with pipeline state objects (PSOs) needed to renderer different @@ -67,6 +124,8 @@ pub struct Renderer { sampler: Sampler, + shadow_map: Option, + skybox_pipeline: GfxPipeline>, figure_pipeline: GfxPipeline>, terrain_pipeline: GfxPipeline>, @@ -74,6 +133,7 @@ pub struct Renderer { sprite_pipeline: GfxPipeline>, particle_pipeline: GfxPipeline>, ui_pipeline: GfxPipeline>, + lod_terrain_pipeline: GfxPipeline>, postprocess_pipeline: GfxPipeline>, player_shadow_pipeline: GfxPipeline>, @@ -81,24 +141,38 @@ pub struct Renderer { noise_tex: Texture<(gfx::format::R8, gfx::format::Unorm)>, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, + mode: RenderMode, } impl Renderer { /// Create a new `Renderer` from a variety of backend-specific components /// and the window targets. pub fn new( - device: gfx_backend::Device, + mut device: gfx_backend::Device, mut factory: gfx_backend::Factory, win_color_view: WinColorView, win_depth_view: WinDepthView, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, + mode: RenderMode, ) -> Result { + // Enable seamless cubemaps globally, where available--they are essentially a + // strict improvement on regular cube maps. + // + // Note that since we only have to enable this once globally, there is no point + // in doing this on rerender. + Self::enable_seamless_cube_maps(&mut device); + + 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), + &ShadowMapMode::try_from(mode.shadow).unwrap_or_default(), + ) + .map_err(|err| { + warn!("Could not create shadow map views: {:?}", err); + }) + .ok(); let ( skybox_pipeline, @@ -108,27 +182,68 @@ impl Renderer { sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, ) = create_pipelines( &mut factory, - aa_mode, - cloud_mode, - fluid_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 = 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, + + directed_depth_stencil_view, + directed_res, + directed_sampler, + + point_pipeline, + terrain_directed_pipeline, + figure_directed_pipeline, + }) + } else { + None + }; let sampler = factory.create_sampler_linear(); let noise_tex = Texture::new( &mut factory, &assets::load_expect("voxygen.texture.noise"), - Some(gfx::texture::FilterMethod::Trilinear), + Some(gfx::texture::FilterMethod::Bilinear), Some(gfx::texture::WrapMode::Tile), + None, )?; Ok(Self { @@ -143,8 +258,11 @@ impl Renderer { tgt_depth_stencil_view, tgt_color_res, + sampler, + shadow_map, + skybox_pipeline, figure_pipeline, terrain_pipeline, @@ -152,6 +270,7 @@ impl Renderer { sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, @@ -159,9 +278,7 @@ impl Renderer { noise_tex, - aa_mode, - cloud_mode, - fluid_mode, + mode, }) } @@ -193,9 +310,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()?; @@ -206,31 +323,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(()) - } + /// 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> { @@ -239,10 +333,35 @@ 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), ShadowMode::Map(mode)) = + (self.shadow_map.as_mut(), self.mode.shadow) + { + match Self::create_shadow_views(&mut self.factory, (dims.0, dims.1), &mode) { + 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) => { + warn!("Could not create shadow map views: {:?}", err); + }, + } + } } Ok(()) @@ -251,9 +370,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) }, @@ -303,6 +422,142 @@ impl Renderer { Ok((tgt_color_view, tgt_depth_stencil_view, tgt_color_res)) } + /// Create textures and views for shadow maps. + // This is a one-use type and the two halves are not guaranteed to remain identical, so we + // disable the type complexity lint. + #[allow(clippy::type_complexity)] + fn create_shadow_views( + factory: &mut gfx_device_gl::Factory, + size: (u16, u16), + mode: &ShadowMapMode, + ) -> Result< + ( + ShadowDepthStencilView, + ShadowResourceView, + Sampler, + ShadowDepthStencilView, + ShadowResourceView, + Sampler, + ), + RenderError, + > { + // (Attempt to) apply resolution factor to shadow map resolution. + let resolution_factor = mode.resolution.clamped(0.25, 4.0); + + let max_texture_size = Self::max_texture_size_raw(factory); + // Limit to max texture size, rather than erroring. + let size = Vec2::new(size.0, size.1).map(|e| { + let size = f32::from(e) * resolution_factor; + // NOTE: We know 0 <= e since we clamped the resolution factor to be between + // 0.25 and 4.0. + if size <= f32::from(max_texture_size) { + size as u16 + } else { + max_texture_size + } + }); + + let levels = 1; + // Limit to max texture size rather than erroring. + let two_size = size.map(|e| { + u16::checked_next_power_of_two(e) + .filter(|&e| e <= max_texture_size) + .unwrap_or(max_texture_size) + }); + 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 { + // Limit to max texture resolution rather than error. + (max_texture_size as u16, max_texture_size as u16) + }; + let diag_two_size = u16::checked_next_power_of_two(diag_size) + .filter(|&e| e <= max_texture_size) + // Limit to max texture resolution rather than error. + .unwrap_or(max_texture_size); + let depth_stencil_cty = <::Channel as gfx::format::ChannelTyped>::get_channel_type(); + + let point_shadow_tex = factory + .create_texture( + gfx::texture::Kind::Cube(diag_two_size / 4), + 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 point_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &point_shadow_tex, + 0, + None, + gfx::texture::DepthStencilFlags::empty(), + )?; + + let point_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &point_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + let directed_shadow_tex = factory + .create_texture( + gfx::texture::Kind::D2(diag_two_size, diag_two_size, 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, + None, + 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, + // Lights should always be assumed to flood areas we can't see. + gfx::texture::WrapMode::Border, + ); + sampler_info.comparison = Some(Comparison::LessEqual); + sampler_info.border = [1.0; 4].into(); + let point_shadow_tex_sampler = factory.create_sampler(sampler_info); + let directed_shadow_tex_sampler = factory.create_sampler(sampler_info); + + Ok(( + point_tgt_shadow_view, + point_tgt_shadow_res, + point_shadow_tex_sampler, + directed_tgt_shadow_view, + directed_tgt_shadow_res, + directed_shadow_tex_sampler, + )) + } + /// Get the resolution of the render target. pub fn get_resolution(&self) -> Vec2 { Vec2::new( @@ -311,14 +566,127 @@ impl Renderer { ) } + /// Get the resolution of the shadow render target. + pub fn get_shadow_resolution(&self) -> (Vec2, Vec2) { + if let Some(shadow_map) = &self.shadow_map { + 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)) + } + } + + /// 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.is_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); + } + } + + /// NOTE: Supported by Vulkan (by default), DirectX 10+ (it seems--it's hard + /// to find proof of this, but Direct3D 10 apparently does it by + /// default, and 11 definitely does, so I assume it's natively supported + /// by DirectX itself), OpenGL 3.2+, and Metal (done by default). While + /// there may be some GPUs that don't quite support it correctly, the + /// impact is relatively small, so there is no reason not to enable it where + /// available. + #[allow(unsafe_code)] + fn enable_seamless_cube_maps(device: &mut gfx_backend::Device) { + unsafe { + // NOTE: Currently just fail silently rather than complain if the computer is on + // a version lower than 3.2, where seamless cubemaps were introduced. + if !device.get_info().is_version_supported(3, 2) { + return; + } + + // NOTE: Safe because GL_TEXTURE_CUBE_MAP_SEAMLESS is supported by OpenGL 3.2+ + // (see https://www.khronos.org/opengl/wiki/Cubemap_Texture#Seamless_cubemap); + // enabling seamless cube maps should always be safe regardless of the state of + // the OpenGL context, so no further checks are needd. + device.with_gl(|gl| { + gl.Enable(gfx_gl::TEXTURE_CUBE_MAP_SEAMLESS); + }); + } + } + + /// 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(device: &mut gfx_backend::Device, 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 !device.get_info().is_version_supported(3, 3) { + 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. + device.with_gl(|gl| { + 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) { 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.is_map() { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + self.encoder.flush(&mut self.device); + Self::set_depth_clamp(&mut self.device, true); + } + } + + /// Perform all queued draw calls for global.shadows. + pub fn flush_shadows(&mut self) { + if !self.mode.shadow.is_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(&mut self.device, false); + } + } + /// Perform all queued draw calls for this frame and clean up discarded /// items. pub fn flush(&mut self) { @@ -335,9 +703,8 @@ impl Renderer { fn recreate_pipelines(&mut self) { match create_pipelines( &mut self.factory, - self.aa_mode, - self.cloud_mode, - self.fluid_mode, + &self.mode, + self.shadow_map.is_some(), &mut self.shader_reload_indicator, ) { Ok(( @@ -348,8 +715,12 @@ impl Renderer { sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, )) => { self.skybox_pipeline = skybox_pipeline; self.figure_pipeline = figure_pipeline; @@ -358,8 +729,24 @@ impl Renderer { self.sprite_pipeline = sprite_pipeline; self.particle_pipeline = particle_pipeline; self.ui_pipeline = ui_pipeline; + self.lod_terrain_pipeline = lod_terrain_pipeline; self.postprocess_pipeline = postprocess_pipeline; self.player_shadow_pipeline = player_shadow_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!(?e, "Could not recreate shaders from assets due to an error",), } @@ -371,7 +758,7 @@ 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) } @@ -381,7 +768,7 @@ impl Renderer { consts: &mut Consts, vals: &[T], ) -> Result<(), RenderError> { - consts.update(&mut self.encoder, vals) + consts.update(&mut self.encoder, vals, 0) } /// Create a new set of instances with the provided values. @@ -418,16 +805,86 @@ 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"))] + /// NOTE: Apparently Macs aren't the only machines that lie. + /// + /// TODO: Find a way to let graphics cards that don't lie do better. + const MAX_TEXTURE_SIZE_MAX: u16 = 8192; + // 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( + pub fn create_texture( &mut self, image: &image::DynamicImage, - filter_method: Option, - wrap_mode: Option, - ) -> Result { - Texture::new(&mut self.factory, image, filter_method, wrap_mode) + filter_method: Option, + wrap_mode: Option, + border: Option, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new(&mut self.factory, image, filter_method, wrap_mode, border) } /// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the @@ -498,8 +955,9 @@ impl Renderer { pub fn render_skybox( &mut self, model: &Model, - globals: &Consts, + global: &GlobalModel, locals: &Consts, + lod: &lod_terrain::LodData, ) { self.encoder.draw( &gfx::Slice { @@ -513,10 +971,12 @@ impl Renderer { &skybox::pipe::Data { vbuf: model.vbuf.clone(), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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) */), }, ); } @@ -524,13 +984,33 @@ impl Renderer { /// Queue the rendering of the provided figure model in the upcoming frame. pub fn render_figure( &mut self, - model: &Model, - globals: &Consts, + model: &figure::FigureModel, + col_lights: &Texture, + global: &GlobalModel, locals: &Consts, bones: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + 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 model = &model.opaque; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -542,14 +1022,20 @@ impl Renderer { &self.figure_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), bones: bones.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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) */), }, ); } @@ -557,13 +1043,34 @@ 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, + _model: &figure::FigureModel, + _col_lights: &Texture, + _global: &GlobalModel, + _bones: &Consts, + _lod: &lod_terrain::LodData, + _locals: &Consts, ) { + // FIXME: Consider reenabling at some point. + /* 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 model = &model.opaque; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -575,28 +1082,54 @@ impl Renderer { &self.player_shadow_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), bones: bones.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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, - globals: &Consts, + model: &figure::FigureModel, + col_lights: &Texture, + global: &GlobalModel, locals: &Consts, bones: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + 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 model = &model.opaque; + self.encoder.draw( &gfx::Slice { start: model.vertex_range().start, @@ -608,14 +1141,20 @@ impl Renderer { &self.figure_pipeline.pso, &figure::pipe::Data { vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), + globals: global.globals.buf.clone(), bones: bones.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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) */), }, ); } @@ -625,11 +1164,30 @@ impl Renderer { pub fn render_terrain_chunk( &mut self, model: &Model, - globals: &Consts, + col_lights: &Texture, + global: &GlobalModel, locals: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + 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, @@ -641,13 +1199,153 @@ 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 global.shadows. + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), locals: locals.buf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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 a shadow map from a point light in the upcoming + /// frame. + pub fn render_shadow_point( + &mut self, + model: &Model, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_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 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, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.point_pipeline.pso, + &shadow::pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: terrain_locals.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.point_depth_stencil_view.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, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_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: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.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: &figure::FigureModel, + global: &GlobalModel, + figure_locals: &Consts, + bones: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_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: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), }, ); } @@ -657,12 +1355,30 @@ impl Renderer { pub fn render_fluid_chunk( &mut self, model: &Model, - globals: &Consts, + global: &GlobalModel, locals: &Consts, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, waves: &Texture, ) { + 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, @@ -675,13 +1391,18 @@ impl Renderer { &fluid::pipe::Data { vbuf: model.vbuf.clone(), locals: locals.buf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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) */), }, ); } @@ -691,11 +1412,32 @@ impl Renderer { pub fn render_sprites( &mut self, model: &Model, - globals: &Consts, + col_lights: &Texture, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, instances: &Instances, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + 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, @@ -708,12 +1450,56 @@ impl Renderer { &sprite::pipe::Data { vbuf: model.vbuf.clone(), ibuf: instances.ibuf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + terrain_locals: terrain_locals.buf.clone(), + // NOTE: It would be nice if this wasn't needed and we could use a constant buffer + // offset into the sprite data. Hopefully, when we switch to wgpu we can do this, + // as it offers the exact API we want (the equivalent can be done in OpenGL using + // glBindBufferOffset). + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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 provided LoD terrain model in the upcoming + /// frame. + pub fn render_lod_terrain( + &mut self, + model: &Model, + global: &GlobalModel, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.lod_terrain_pipeline.pso, + &lod_terrain::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + map: (lod.map.srv.clone(), lod.map.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), }, ); } @@ -722,11 +1508,29 @@ impl Renderer { pub fn render_particles( &mut self, model: &Model, - globals: &Consts, + global: &GlobalModel, instances: &Instances, - lights: &Consts, - shadows: &Consts, + lod: &lod_terrain::LodData, ) { + 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, @@ -739,37 +1543,46 @@ impl Renderer { &particle::pipe::Data { vbuf: model.vbuf.clone(), ibuf: instances.ibuf.clone(), - globals: globals.buf.clone(), - lights: lights.buf.clone(), - shadows: shadows.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.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 provided UI element in the upcoming frame. - pub fn render_ui_element( + pub fn render_ui_element>( &mut self, - model: &Model, - tex: &Texture, + model: Model, + tex: &Texture, scissor: Aabr, globals: &Consts, locals: &Consts, - ) { + ) where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { let Aabr { min, max } = scissor; self.encoder.draw( &gfx::Slice { - start: model.vertex_range().start, - end: model.vertex_range().end, + start: model.vertex_range.start, + end: model.vertex_range.end, base_vertex: 0, instances: None, buffer: gfx::IndexBuffer::Auto, }, &self.ui_pipeline.pso, &ui::pipe::Data { - vbuf: model.vbuf.clone(), + vbuf: model.vbuf, scissor: gfx::Rect { x: min.x, y: min.y, @@ -820,9 +1633,8 @@ struct GfxPipeline { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 fn create_pipelines( factory: &mut gfx_backend::Factory, - aa_mode: AaMode, - cloud_mode: CloudMode, - fluid_mode: FluidMode, + mode: &RenderMode, + has_shadow_views: bool, shader_reload_indicator: &mut ReloadIndicator, ) -> Result< ( @@ -833,11 +1645,20 @@ fn create_pipelines( GfxPipeline>, GfxPipeline>, GfxPipeline>, + GfxPipeline>, GfxPipeline>, GfxPipeline>, + Option>>, + Option>>, + Option>>, ), RenderError, > { + let constants = assets::load_watched::( + "voxygen.shaders.include.constants", + shader_reload_indicator, + ) + .unwrap(); let globals = assets::load_watched::("voxygen.shaders.include.globals", shader_reload_indicator) .unwrap(); @@ -853,9 +1674,50 @@ fn create_pipelines( let random = assets::load_watched::("voxygen.shaders.include.random", shader_reload_indicator) .unwrap(); + 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!( + r#" +{} + +#define VOXYGEN_COMPUTATION_PREERENCE {} +#define FLUID_MODE {} +#define CLOUD_MODE {} +#define LIGHTING_ALGORITHM {} +#define SHADOW_MODE {} + +"#, + constants, + // TODO: Configurable vertex/fragment shader preference. + "VOXYGEN_COMPUTATION_PREERENCE_FRAGMENT", + match mode.fluid { + FluidMode::Cheap => "FLUID_MODE_CHEAP", + FluidMode::Shiny => "FLUID_MODE_SHINY", + }, + match mode.cloud { + CloudMode::None => "CLOUD_MODE_NONE", + CloudMode::Regular => "CLOUD_MODE_REGULAR", + }, + match mode.lighting { + LightingMode::Ashikhmin => "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", @@ -868,7 +1730,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", }] @@ -878,14 +1740,45 @@ fn create_pipelines( .unwrap(); 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); include_ctx.include("random.glsl", &random); + include_ctx.include("lod.glsl", &lod); 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_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, @@ -902,8 +1795,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, @@ -929,7 +1821,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", }] @@ -977,6 +1869,24 @@ fn create_pipelines( gfx::state::CullFace::Back, )?; + // Construct a pipeline for rendering terrain + let lod_terrain_pipeline = create_pipeline( + factory, + lod_terrain::pipe::new(), + &assets::load_watched::( + "voxygen.shaders.lod-terrain-vert", + shader_reload_indicator, + ) + .unwrap(), + &assets::load_watched::( + "voxygen.shaders.lod-terrain-frag", + shader_reload_indicator, + ) + .unwrap(), + &include_ctx, + gfx::state::CullFace::Back, + )?; + // Construct a pipeline for rendering our post-processing let postprocess_pipeline = create_pipeline( factory, @@ -999,18 +1909,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, @@ -1020,6 +1927,76 @@ fn create_pipelines( gfx::state::CullFace::Back, )?; + // Construct a pipeline for rendering point light terrain shadow maps. + let point_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::pipe::new(), + &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, + ) + .unwrap(), + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 0)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + 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_frag, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 1)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + 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_frag, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 1)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!( + "Could not load directed figure shadow map pipeline: {:?}", + err + ); + None + }, + }; + Ok(( skybox_pipeline, figure_pipeline, @@ -1028,14 +2005,17 @@ fn create_pipelines( sprite_pipeline, particle_pipeline, ui_pipeline, + lod_terrain_pipeline, postprocess_pipeline, player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, )) } /// Create a new pipeline from the provided vertex shader and fragment shader. -#[allow(clippy::extra_unused_lifetimes)] // TODO: Pending review in #587 -fn create_pipeline<'a, P: gfx::pso::PipelineInit>( +fn create_pipeline( factory: &mut gfx_backend::Factory, pipe: P, vs: &str, @@ -1048,7 +2028,7 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>( let program = factory.link_program(vs.as_bytes(), fs.as_bytes())?; - Ok(GfxPipeline { + let result = Ok(GfxPipeline { pso: factory.create_pipeline_from_program( &program, gfx::Primitive::TriangleList, @@ -1061,5 +2041,51 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>( }, pipe, )?, + }); + + result +} + +/// Create a new shadow map pipeline. +fn create_shadow_pipeline( + factory: &mut gfx_backend::Factory, + pipe: P, + vs: &str, + gs: Option<&str>, + fs: &str, + ctx: &IncludeContext, + cull_face: gfx::state::CullFace, + offset: Option, +) -> Result, RenderError> { + let vs = ctx.expand(vs)?; + let gs = gs.map(|gs| ctx.expand(gs)).transpose()?; + let fs = ctx.expand(fs)?; + + 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())? + }; + + Ok(GfxPipeline { + pso: factory.create_pipeline_state( + &shader_set, + gfx::Primitive::TriangleList, + gfx::state::Rasterizer { + front_face: gfx::state::FrontFace::CounterClockwise, + // Second-depth shadow mapping: should help reduce z-fighting provided all objects + // are "watertight" (every triangle edge is shared with at most one other + // triangle); this *should* be true for Veloren. + cull_face: match cull_face { + gfx::state::CullFace::Front => gfx::state::CullFace::Back, + gfx::state::CullFace::Back => gfx::state::CullFace::Front, + gfx::state::CullFace::Nothing => gfx::state::CullFace::Nothing, + }, + method: gfx::state::RasterMethod::Fill, + offset, + samples: None, + }, + pipe, + )?, }) } diff --git a/voxygen/src/render/texture.rs b/voxygen/src/render/texture.rs index f166b9d2c4..1c4cb1fa50 100644 --- a/voxygen/src/render/texture.rs +++ b/voxygen/src/render/texture.rs @@ -31,7 +31,14 @@ where image: &DynamicImage, filter_method: Option, wrap_mode: Option, + border: Option, ) -> Result { + // TODO: Actualy handle images that aren't in rgba format properly. + let buffer = image.as_flat_samples_u8().ok_or_else(|| { + RenderError::CustomError( + "We currently do not support color formats using more than 4 bytes / pixel.".into(), + ) + })?; let (tex, srv) = factory .create_texture_immutable_u8::( gfx::texture::Kind::D2( @@ -40,17 +47,24 @@ where gfx::texture::AaMode::Single, ), gfx::texture::Mipmap::Provided, - &[&image.raw_pixels()], + // Guarenteed to be correct, since all the conversions from DynamicImage to + // FlatSamples go through the underlying ImageBuffer's implementation of + // as_flat_samples(), which guarantees that the resulting FlatSamples is + // well-formed. + &[buffer.as_slice()], ) .map_err(RenderError::CombinedError)?; + let mut sampler_info = gfx::texture::SamplerInfo::new( + filter_method.unwrap_or(gfx::texture::FilterMethod::Scale), + wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp), + ); + let transparent = [0.0, 0.0, 0.0, 1.0].into(); + sampler_info.border = border.unwrap_or(transparent); Ok(Self { tex, srv, - sampler: factory.create_sampler(gfx::texture::SamplerInfo::new( - filter_method.unwrap_or(gfx::texture::FilterMethod::Scale), - wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp), - )), + sampler: factory.create_sampler(sampler_info), }) } @@ -86,6 +100,56 @@ 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(RenderError::CombinedError)?; + + 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)))?; + + 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). diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index b738aeb511..9d68f02ef1 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -137,10 +137,16 @@ fn handle_main_events_cleared( } if let Some(last) = states.last_mut() { - global_state.window.renderer_mut().clear(); - last.render(global_state.window.renderer_mut(), &global_state.settings); + let renderer = global_state.window.renderer_mut(); + // Clear the shadow maps. + renderer.clear_shadows(); + // Clear the screen + renderer.clear(); + // Render the screen using the global renderer + last.render(renderer, &global_state.settings); // Finish the frame. global_state.window.renderer_mut().flush(); + // Display the frame on the window. global_state .window .swap_buffers() diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index deaadcc963..a0fa8af2be 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.25; +pub const FAR_PLANE: f32 = 100000.0; const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; @@ -46,6 +46,7 @@ pub struct Camera { last_time: Option, dependents: Dependents, + frustum: Frustum, } impl Camera { @@ -70,6 +71,7 @@ impl Camera { cam_pos: Vec3::zero(), cam_dir: Vec3::unit_y(), }, + frustum: Frustum::from_modelview_projection(Mat4::identity().into_col_arrays()), } } @@ -99,22 +101,24 @@ 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(), + ); self.dependents.cam_dir = Vec3::from(self.dependents.view_mat.inverted() * -Vec4::unit_z()); } - 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 } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 98ee3824e3..4a9526dc58 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -1,85 +1,166 @@ -use super::load::*; +use super::{load::*, FigureModelEntry}; use crate::{ - mesh::Meshable, - render::{FigurePipeline, Mesh, Model, Renderer}, + mesh::{greedy::GreedyMesh, Meshable}, + render::{BoneMeshes, FigureModel, FigurePipeline, Mesh, Renderer, TerrainPipeline}, scene::camera::CameraMode, }; use anim::Skeleton; use common::{ assets::watch::ReloadIndicator, comp::{ - item::{armor::ArmorKind, tool::ToolKind, ItemKind}, + item::{ + armor::{Armor, ArmorKind}, + tool::ToolKind, + ItemKind, + }, Body, CharacterState, Loadout, }, figure::Segment, vol::BaseVol, }; +use core::convert::TryInto; use hashbrown::{hash_map::Entry, HashMap}; -use std::{ - convert::TryInto, - mem::{discriminant, Discriminant}, -}; use vek::*; -#[allow(clippy::large_enum_variant)] // TODO: Pending review in #587 -#[derive(PartialEq, Eq, Hash, Clone)] -enum FigureKey { - Simple(Body), - Complex(Body, CameraMode, CharacterCacheKey), +pub type FigureModelEntryLod = FigureModelEntry<3>; + +#[derive(Eq, Hash, PartialEq)] +struct FigureKey { + /// Body pointed to by this key. + body: Body, + /// Extra state. + extra: Option>, } -#[derive(PartialEq, Eq, Hash, Clone)] +/// Character data that should be visible when tools are visible (i.e. in third +/// person or when the character is in a tool-using state). +#[derive(Eq, Hash, PartialEq)] +pub struct CharacterToolKey { + active: Option, + second: Option, +} + +/// Character data that exists in third person only. +#[derive(Eq, Hash, PartialEq)] +struct CharacterThirdPersonKey { + shoulder: Option, + chest: Option, + belt: Option, + back: Option, + pants: Option, +} + +#[derive(Eq, Hash, PartialEq)] +/// NOTE: To avoid spamming the character cache with player models, we try to +/// store only the minimum information required to correctly update the model. +/// +/// TODO: Memoize, etc. struct CharacterCacheKey { - state: Option>, // TODO: Can this be simplified? - active_tool: Option, - second_tool: Option, - shoulder: Option, - chest: Option, - belt: Option, - back: Option, + /// Character state that is only visible in third person. + third_person: Option, + /// Tool state should be present when a character is either in third person, + /// or is in first person and the character state is tool-using. + /// + /// NOTE: This representation could be tightened in various ways to + /// eliminate incorrect states, e.g. setting active_tool to None when no + /// tools are equipped, but currently we are more focused on the big + /// performance impact of recreating the whole model whenever the character + /// state changes, so for now we don't bother with this. + tool: Option, lantern: Option, - hand: Option, - pants: Option, - foot: Option, + hand: Option, + foot: Option, } impl CharacterCacheKey { - fn from(cs: Option<&CharacterState>, loadout: &Loadout) -> Self { + fn from(cs: Option<&CharacterState>, camera_mode: CameraMode, loadout: &Loadout) -> Self { + let is_first_person = match camera_mode { + CameraMode::FirstPerson => true, + CameraMode::ThirdPerson | CameraMode::Freefly => false, + }; + + // Third person tools are only modeled when the camera is either not first + // person, or the camera is first person and we are in a tool-using + // state. + let are_tools_visible = !is_first_person + || cs + .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) + // If there's no provided character state but we're still somehow in first person, + // We currently assume there's no need to visually model tools. + // + // TODO: Figure out what to do here, and/or refactor how this works. + .unwrap_or(false); + Self { - state: cs.map(|cs| discriminant(cs)), - active_tool: if let Some(ItemKind::Tool(tool)) = - loadout.active_item.as_ref().map(|i| &i.item.kind) - { - Some(tool.kind.clone()) - } else { + // Third person armor is only modeled when the camera mode is not first person. + third_person: if is_first_person { None - }, - second_tool: if let Some(ItemKind::Tool(tool)) = - loadout.second_item.as_ref().map(|i| &i.item.kind) - { - Some(tool.kind.clone()) } else { - None + Some(CharacterThirdPersonKey { + shoulder: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Shoulder(armor), + .. + })) = loadout.shoulder.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + chest: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Chest(armor), + .. + })) = loadout.chest.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + belt: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Belt(armor), + .. + })) = loadout.belt.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + back: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Back(armor), + .. + })) = loadout.back.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + pants: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Pants(armor), + .. + })) = loadout.pants.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) + } else { + None + }, + }) }, - shoulder: if let Some(ItemKind::Armor(armor)) = - loadout.shoulder.as_ref().map(|i| &i.kind) - { - Some(armor.kind.clone()) - } else { - None - }, - chest: if let Some(ItemKind::Armor(armor)) = loadout.chest.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) - } else { - None - }, - belt: if let Some(ItemKind::Armor(armor)) = loadout.belt.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) - } else { - None - }, - back: if let Some(ItemKind::Armor(armor)) = loadout.back.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) + tool: if are_tools_visible { + Some(CharacterToolKey { + active: if let Some(ItemKind::Tool(tool)) = + loadout.active_item.as_ref().map(|i| &i.item.kind) + { + Some(tool.kind.clone()) + } else { + None + }, + second: if let Some(ItemKind::Tool(tool)) = + loadout.second_item.as_ref().map(|i| &i.item.kind) + { + Some(tool.kind.clone()) + } else { + None + }, + }) } else { None }, @@ -90,18 +171,21 @@ impl CharacterCacheKey { } else { None }, - hand: if let Some(ItemKind::Armor(armor)) = loadout.hand.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) + hand: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Hand(armor), + .. + })) = loadout.hand.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) } else { None }, - pants: if let Some(ItemKind::Armor(armor)) = loadout.pants.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) - } else { - None - }, - foot: if let Some(ItemKind::Armor(armor)) = loadout.foot.as_ref().map(|i| &i.kind) { - Some(armor.kind.clone()) + foot: if let Some(ItemKind::Armor(Armor { + kind: ArmorKind::Foot(armor), + .. + })) = loadout.foot.as_ref().map(|i| &i.kind) + { + Some(armor.clone()) } else { None }, @@ -114,7 +198,7 @@ pub struct FigureModelCache where Skel: Skeleton, { - models: HashMap; 3], Skel::Attr), u64)>, + models: HashMap, manifest_indicator: ReloadIndicator, } @@ -127,16 +211,18 @@ impl FigureModelCache { } } + /// NOTE: We deliberately call this function with only the key into the + /// cache, to enforce that the cached state only depends on the key. We + /// may end up using a mechanism different from this cache eventually, + /// in which case this strategy might change. fn bone_meshes( - body: Body, - loadout: Option<&Loadout>, - character_state: Option<&CharacterState>, - camera_mode: CameraMode, + FigureKey { body, extra }: &FigureKey, 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_color_spec = HumColorSpec::load_watched(manifest_indicator); let humanoid_head_spec = HumHeadSpec::load_watched(manifest_indicator); let humanoid_armor_shoulder_spec = HumArmorShoulderSpec::load_watched(manifest_indicator); @@ -150,94 +236,126 @@ impl FigureModelCache { let humanoid_armor_foot_spec = HumArmorFootSpec::load_watched(manifest_indicator); let humanoid_main_weapon_spec = HumMainWeaponSpec::load_watched(manifest_indicator); + const DEFAULT_LOADOUT: CharacterCacheKey = CharacterCacheKey { + third_person: None, + tool: None, + lantern: None, + hand: None, + foot: None, + }; + // TODO: This is bad code, maybe this method should return Option<_> - let default_loadout = Loadout::default(); - let loadout = loadout.unwrap_or(&default_loadout); + let loadout = extra.as_deref().unwrap_or(&DEFAULT_LOADOUT); + let third_person = loadout.third_person.as_ref(); + let tool = loadout.tool.as_ref(); + let lantern = loadout.lantern.as_deref(); + let hand = loadout.hand.as_deref(); + let foot = loadout.foot.as_deref(); [ - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_head_spec.mesh_head(&body, generate_mesh)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => Some( - humanoid_armor_chest_spec.mesh_chest(&body, loadout, generate_mesh), - ), - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout, generate_mesh)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_back_spec.mesh_back(&body, loadout, generate_mesh)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => Some( - humanoid_armor_pants_spec.mesh_pants(&body, loadout, generate_mesh), - ), - CameraMode::FirstPerson => None, - }, - Some(humanoid_armor_hand_spec.mesh_left_hand(&body, loadout, generate_mesh)), - Some(humanoid_armor_hand_spec.mesh_right_hand(&body, loadout, generate_mesh)), - Some(humanoid_armor_foot_spec.mesh_left_foot(&body, loadout, generate_mesh)), - Some(humanoid_armor_foot_spec.mesh_right_foot(&body, loadout, generate_mesh)), - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_shoulder_spec.mesh_left_shoulder( - &body, - loadout, - generate_mesh, - )) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson | CameraMode::Freefly => { - Some(humanoid_armor_shoulder_spec.mesh_right_shoulder( - &body, - loadout, - generate_mesh, - )) - }, - CameraMode::FirstPerson => None, - }, - Some(mesh_glider(generate_mesh)), - if camera_mode != CameraMode::FirstPerson - || character_state - .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) - .unwrap_or_default() - { - Some(humanoid_main_weapon_spec.mesh_main_weapon( - loadout.active_item.as_ref().map(|i| &i.item.kind), + third_person.map(|_| { + humanoid_head_spec.mesh_head( + body, + &humanoid_color_spec, + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_chest_spec.mesh_chest( + body, + &humanoid_color_spec, + loadout.chest.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_belt_spec.mesh_belt( + body, + &humanoid_color_spec, + loadout.belt.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_back_spec.mesh_back( + body, + &humanoid_color_spec, + loadout.back.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_pants_spec.mesh_pants( + body, + &humanoid_color_spec, + loadout.pants.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + Some(humanoid_armor_hand_spec.mesh_left_hand( + body, + &humanoid_color_spec, + hand, + |segment, offset| generate_mesh(segment, offset), + )), + Some(humanoid_armor_hand_spec.mesh_right_hand( + body, + &humanoid_color_spec, + hand, + |segment, offset| generate_mesh(segment, offset), + )), + Some(humanoid_armor_foot_spec.mesh_left_foot( + body, + &humanoid_color_spec, + foot, + |segment, offset| generate_mesh(segment, offset), + )), + Some(humanoid_armor_foot_spec.mesh_right_foot( + body, + &humanoid_color_spec, + foot, + |segment, offset| generate_mesh(segment, offset), + )), + third_person.map(|loadout| { + humanoid_armor_shoulder_spec.mesh_left_shoulder( + body, + &humanoid_color_spec, + loadout.shoulder.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + third_person.map(|loadout| { + humanoid_armor_shoulder_spec.mesh_right_shoulder( + body, + &humanoid_color_spec, + loadout.shoulder.as_deref(), + |segment, offset| generate_mesh(segment, offset), + ) + }), + Some(mesh_glider(|segment, offset| { + generate_mesh(segment, offset) + })), + tool.map(|tool| { + humanoid_main_weapon_spec.mesh_main_weapon( + tool.active.as_ref(), false, - generate_mesh, - )) - } else { - None - }, - if camera_mode != CameraMode::FirstPerson - || character_state - .map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield()) - .unwrap_or_default() - { - Some(humanoid_main_weapon_spec.mesh_main_weapon( - loadout.second_item.as_ref().map(|i| &i.item.kind), + |segment, offset| generate_mesh(segment, offset), + ) + }), + tool.map(|tool| { + humanoid_main_weapon_spec.mesh_main_weapon( + tool.second.as_ref(), true, - generate_mesh, - )) - } else { - None - }, - Some(humanoid_armor_lantern_spec.mesh_lantern(&body, loadout, generate_mesh)), - Some(mesh_hold(generate_mesh)), + |segment, offset| generate_mesh(segment, offset), + ) + }), + Some(humanoid_armor_lantern_spec.mesh_lantern( + body, + &humanoid_color_spec, + lantern, + |segment, offset| generate_mesh(segment, offset), + )), + Some(mesh_hold(|segment, offset| generate_mesh(segment, offset))), ] }, Body::QuadrupedSmall(body) => { @@ -250,37 +368,37 @@ 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_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_small_central_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -303,77 +421,77 @@ 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_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_central_spec.mesh_torso_back( 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_leg_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_leg_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_medium_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, ] @@ -388,37 +506,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, @@ -432,12 +550,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, @@ -457,82 +587,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, @@ -547,8 +689,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, @@ -574,77 +720,77 @@ 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_jaw( 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_center_spec.mesh_tail( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_main( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(biped_large_center_spec.mesh_second( 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, ] @@ -654,51 +800,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, @@ -715,27 +865,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, @@ -760,52 +910,52 @@ impl FigureModelCache { Some(quadruped_low_central_spec.mesh_head_upper( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_head_lower( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_jaw( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_chest( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_tail_front( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_central_spec.mesh_tail_rear( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_fl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_fr( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_bl( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), Some(quadruped_low_lateral_spec.mesh_foot_br( body.species, body.body_type, - generate_mesh, + |segment, offset| generate_mesh(segment, offset), )), None, None, @@ -839,24 +989,26 @@ 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) + ) -> &(FigureModelEntryLod, Skel::Attr) where for<'a> &'a common::comp::Body: std::convert::TryInto, Skel::Attr: Default, { - let key = if let Some(loadout) = loadout { - FigureKey::Complex( - body, - camera_mode, - CharacterCacheKey::from(character_state, loadout), - ) - } else { - FigureKey::Simple(body) + let key = FigureKey { + body, + extra: loadout.map(|loadout| { + Box::new(CharacterCacheKey::from( + character_state, + camera_mode, + loadout, + )) + }), }; match self.models.entry(key) { @@ -866,92 +1018,171 @@ impl FigureModelCache { model }, Entry::Vacant(v) => { - &v.insert(( - { - let skeleton_attr = (&body) - .try_into() - .ok() - .unwrap_or_else(::default); + let key = v.key(); + let model = { + let skeleton_attr = (&body) + .try_into() + .ok() + .unwrap_or_else(::default); - let manifest_indicator = &mut self.manifest_indicator; - let mut make_model = |generate_mesh| { - let mut mesh = Mesh::new(); - Self::bone_meshes( - body, - loadout, - character_state, - camera_mode, - manifest_indicator, - generate_mesh, - ) + let manifest_indicator = &mut self.manifest_indicator; + let mut greedy = FigureModel::make_greedy(); + let mut opaque = Mesh::::new(); + // Choose the most conservative bounds for any LOD model. + let mut figure_bounds = anim::vek::Aabb { + min: anim::vek::Vec3::zero(), + max: anim::vek::Vec3::zero(), + }; + // Meshes all bone models for this figure using the given mesh generation + // function, attaching it to the current greedy mesher and opaque vertex + // list. Returns the vertex bounds of the meshed model within the opaque + // mesh. + let mut make_model = |generate_mesh: for<'a, 'b> fn( + &mut GreedyMesh<'a>, + &'b mut _, + _, + _, + ) + -> _| { + let vertex_start = opaque.vertices().len(); + let meshes = + Self::bone_meshes(key, manifest_indicator, |segment, offset| { + generate_mesh(&mut greedy, &mut opaque, segment, offset) + }); + meshes .iter() .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)) + // NOTE: Cast to u8 is safe because i <= 16. + .filter_map(|(i, bm)| bm.as_ref().map(|bm| (i as u8, bm.clone()))) + .for_each(|(i, (_opaque_mesh, (bounds, vertex_range)))| { + // Update the bone index for all vertices that belong to this + // model. + opaque + .iter_mut(vertex_range) + .for_each(|vert| { + vert.set_bone_idx(i); + }); + + // Update the figure bounds to the largest granularity seen so far + // (NOTE: this is more than a little imeprfect). + // + // FIXME: Maybe use the default bone position in the idle animation + // to figure this out instead? + figure_bounds.expand_to_contain(bounds); }); - renderer.create_model(&mesh).unwrap() - }; + // NOTE: vertex_start and vertex_end *should* fit in a u32, by the + // following logic: + // + // Our new figure maximum is constrained to at most 2^8 × 2^8 × 2^8. + // This uses at most 24 bits to store every vertex exactly once. + // Greedy meshing can store each vertex in up to 3 quads, we have 3 + // greedy models, and we store 1.5x the vertex count, so the maximum + // total space a model can take up is 3 * 3 * 1.5 * 2^24; rounding + // up to 4 * 4 * 2^24 gets us to 2^28, which clearly still fits in a + // u32. + // + // (We could also, though we prefer not to, reason backwards from the + // maximum figure texture size of 2^15 × 2^15, also fits in a u32; we + // can also see that, since we can have at most one texture entry per + // vertex, any texture atlas of size 2^14 × 2^14 or higher should be + // able to store data for any figure. So the only reason we would fail + // here would be if the user's computer could not store a texture large + // enough to fit all the LOD models for the figure, not for fundamental + // reasons related to fitting in a u32). + // + // Therefore, these casts are safe. + vertex_start as u32..opaque.vertices().len() as u32 + }; - fn generate_mesh( - segment: &Segment, - offset: Vec3, - ) -> Mesh { - Meshable::::generate_mesh( + fn generate_mesh<'a>( + greedy: &mut GreedyMesh<'a>, + opaque_mesh: &mut Mesh, + segment: Segment, + offset: Vec3, + ) -> BoneMeshes { + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( segment, - (offset, Vec3::one()), - ) - .0 - } + (greedy, opaque_mesh, offset, Vec3::one()), + ); + (opaque, bounds) + } - fn generate_mesh_lod_mid( - segment: &Segment, - offset: Vec3, - ) -> Mesh { - let lod_scale = Vec3::broadcast(0.6); - Meshable::::generate_mesh( - &segment.scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0 - } + fn generate_mesh_lod_mid<'a>( + greedy: &mut GreedyMesh<'a>, + opaque_mesh: &mut Mesh, + segment: Segment, + offset: Vec3, + ) -> BoneMeshes { + let lod_scale = 0.6; + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment.scaled_by(Vec3::broadcast(lod_scale)), + ( + greedy, + opaque_mesh, + offset * lod_scale, + Vec3::one() / lod_scale, + ), + ); + (opaque, bounds) + } - fn generate_mesh_lod_low( - segment: &Segment, - offset: Vec3, - ) -> Mesh { - let lod_scale = Vec3::broadcast(0.3); - Meshable::::generate_mesh( - &segment.scaled_by(lod_scale), - (offset * lod_scale, Vec3::one() / lod_scale), - ) - .0 - } + fn generate_mesh_lod_low<'a>( + greedy: &mut GreedyMesh<'a>, + opaque_mesh: &mut Mesh, + segment: Segment, + offset: Vec3, + ) -> BoneMeshes { + let lod_scale = 0.3; + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment.scaled_by(Vec3::broadcast(lod_scale)), + ( + greedy, + opaque_mesh, + offset * lod_scale, + Vec3::one() / lod_scale, + ), + ); + (opaque, bounds) + } - ( - [ - make_model(generate_mesh), - make_model(generate_mesh_lod_mid), - make_model(generate_mesh_lod_low), - ], - skeleton_attr, - ) - }, - tick, - )) - .0 + let models = [ + make_model(generate_mesh), + make_model(generate_mesh_lod_mid), + make_model(generate_mesh_lod_low), + ]; + ( + col_lights + .create_figure(renderer, greedy, (opaque, figure_bounds), models) + .expect("Failed to upload figure data to the GPU!"), + skeleton_attr, + ) + }; + &v.insert((model, tick)).0 }, } } - 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(|_, ((model_entry, _), last_used)| { + // Wait about a minute at 60 fps before invalidating old models. + let delta = 60 * 60; + let alive = *last_used + delta > tick; + if !alive { + col_lights.atlas.deallocate(model_entry.allocation.id); + } + alive + }); + } } } diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index e03d9ff608..c499f6d29d 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::{ @@ -9,17 +9,12 @@ use common::{ dragon::{BodyType as DBodyType, Species as DSpecies}, fish_medium, fish_small, golem::{BodyType as GBodyType, Species as GSpecies}, - humanoid::{Body, BodyType, EyeColor, Skin, Species}, - item::{ - armor::{Armor, ArmorKind}, - tool::{Tool, ToolKind}, - ItemKind, Lantern, - }, + humanoid::{self, Body, BodyType, EyeColor, Skin, Species}, + item::tool::ToolKind, object, quadruped_low::{BodyType as QLBodyType, Species as QLSpecies}, quadruped_medium::{BodyType as QMBodyType, Species as QMSpecies}, quadruped_small::{BodyType as QSBodyType, Species as QSSpecies}, - Loadout, }, figure::{DynaUnionizer, MatSegment, Material, Segment}, }; @@ -60,28 +55,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) -} - -fn color_segment( - mat_segment: MatSegment, - skin: Skin, - hair_color: Rgb, - eye_color: EyeColor, -) -> Segment { - // TODO move some of the colors to common - mat_segment.to_segment(|mat| match mat { - Material::Skin => skin.rgb(), - Material::SkinDark => skin.dark_rgb(), - Material::SkinLight => skin.light_rgb(), - Material::Hair => hair_color, - // TODO add back multiple colors - Material::EyeLight => eye_color.light_rgb(), - Material::EyeDark => eye_color.dark_rgb(), - Material::EyeWhite => eye_color.white_rgb(), - }) + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, +) -> BoneMeshes { + generate_mesh(load_segment(mesh_name), position) } fn recolor_grey(rgb: Rgb, color: Rgb) -> Rgb { @@ -126,6 +102,63 @@ struct MobSidedVoxSpec { right: ArmorVoxSpec, } +/// Color information not found in voxels, for humanoids. +#[derive(Serialize, Deserialize)] +pub struct HumColorSpec { + hair_colors: humanoid::species::PureCases>, + eye_colors_light: humanoid::eye_color::PureCases<(u8, u8, u8)>, + eye_colors_dark: humanoid::eye_color::PureCases<(u8, u8, u8)>, + eye_white: (u8, u8, u8), + skin_colors_plain: humanoid::skin::PureCases<(u8, u8, u8)>, + skin_colors_light: humanoid::skin::PureCases<(u8, u8, u8)>, + skin_colors_dark: humanoid::skin::PureCases<(u8, u8, u8)>, +} + +impl Asset for HumColorSpec { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) + } +} + +impl HumColorSpec { + pub fn load_watched(indicator: &mut ReloadIndicator) -> Arc { + assets::load_watched::("voxygen.voxel.humanoid_color_manifest", indicator).unwrap() + } + + fn hair_color(&self, species: Species, val: u8) -> (u8, u8, u8) { + species + .elim_case_pure(&self.hair_colors) + .get(val as usize) + .copied() + .unwrap_or((0, 0, 0)) + } + + fn color_segment( + &self, + mat_segment: MatSegment, + skin: Skin, + hair_color: (u8, u8, u8), + eye_color: EyeColor, + ) -> Segment { + // TODO move some of the colors to common + mat_segment.to_segment(|mat| { + match mat { + Material::Skin => *skin.elim_case_pure(&self.skin_colors_plain), + Material::SkinDark => *skin.elim_case_pure(&self.skin_colors_dark), + Material::SkinLight => *skin.elim_case_pure(&self.skin_colors_light), + Material::Hair => hair_color, + // TODO add back multiple colors + Material::EyeLight => *eye_color.elim_case_pure(&self.eye_colors_light), + Material::EyeDark => *eye_color.elim_case_pure(&self.eye_colors_dark), + Material::EyeWhite => self.eye_white, + } + .into() + }) + } +} + // All reliant on humanoid::Species and humanoid::BodyType #[derive(Serialize, Deserialize)] struct HumHeadSubSpec { @@ -155,8 +188,9 @@ impl HumHeadSpec { pub fn mesh_head( &self, body: &Body, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { + color_spec: &HumColorSpec, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { let spec = match self.0.get(&(body.species, body.body_type)) { Some(spec) => spec, None => { @@ -169,7 +203,8 @@ impl HumHeadSpec { }, }; - let hair_rgb = body.species.hair_color(body.hair_color); + let hair_color = color_spec.hair_color(body.species, body.hair_color); + let hair_rgb = hair_color.into(); let skin_rgb = body.species.skin_color(body.skin); let eye_rgb = body.species.eye_color(body.eye_color); @@ -178,10 +213,10 @@ impl HumHeadSpec { let eyes = match spec.eyes.get(body.eyes as usize) { Some(Some(spec)) => Some(( - color_segment( + color_spec.color_segment( graceful_load_mat_segment(&spec.0).map_rgb(|rgb| recolor_grey(rgb, hair_rgb)), skin_rgb, - hair_rgb, + hair_color, eye_rgb, ), Vec3::from(spec.1), @@ -225,7 +260,7 @@ impl HumHeadSpec { let (head, origin_offset) = DynaUnionizer::new() .add( - color_segment(bare_head, skin_rgb, hair_rgb, eye_rgb), + color_spec.color_segment(bare_head, skin_rgb, hair_color, eye_rgb), spec.head.1.into(), ) .maybe_add(eyes) @@ -235,7 +270,7 @@ impl HumHeadSpec { .unify(); generate_mesh( - &head, + head, Vec3::from(spec.offset) + origin_offset.map(|e| e as f32 * -1.0), ) } @@ -361,15 +396,12 @@ impl HumArmorShoulderSpec { fn mesh_shoulder( &self, body: &Body, - loadout: &Loadout, + color_spec: &HumColorSpec, + shoulder: Option<&str>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Shoulder(shoulder), - .. - })) = loadout.shoulder.as_ref().map(|i| &i.kind) - { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(shoulder) = shoulder { match self.0.map.get(shoulder) { Some(spec) => spec, None => { @@ -381,14 +413,14 @@ impl HumArmorShoulderSpec { &self.0.default }; - let mut shoulder_segment = color_segment( + let mut shoulder_segment = color_spec.color_segment( if flipped { graceful_load_mat_segment_flipped(&spec.left.vox_spec.0) } else { graceful_load_mat_segment(&spec.right.vox_spec.0) }, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -413,25 +445,27 @@ 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 { - self.mesh_shoulder(body, loadout, true, generate_mesh) + color_spec: &HumColorSpec, + shoulder: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_shoulder(body, color_spec, shoulder, true, generate_mesh) } pub fn mesh_right_shoulder( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_shoulder(body, loadout, false, generate_mesh) + color_spec: &HumColorSpec, + shoulder: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_shoulder(body, color_spec, shoulder, false, generate_mesh) } } // Chest @@ -444,18 +478,15 @@ impl HumArmorChestSpec { pub fn mesh_chest( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Chest(chest), - .. - })) = loadout.chest.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + chest: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(chest) = chest { match self.0.map.get(chest) { Some(spec) => spec, None => { - error!(?loadout.chest, "No chest specification exists"); + error!(?chest, "No chest specification exists"); return load_mesh("not_found", Vec3::new(-7.0, -3.5, 2.0), generate_mesh); }, } @@ -464,10 +495,10 @@ impl HumArmorChestSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -487,7 +518,7 @@ impl HumArmorChestSpec { .unify() .0; - generate_mesh(&chest, Vec3::from(spec.vox_spec.1)) + generate_mesh(chest, Vec3::from(spec.vox_spec.1)) } } // Hand @@ -500,15 +531,12 @@ impl HumArmorHandSpec { fn mesh_hand( &self, body: &Body, - loadout: &Loadout, + color_spec: &HumColorSpec, + hand: Option<&str>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Hand(hand), - .. - })) = loadout.hand.as_ref().map(|i| &i.kind) - { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(hand) = hand { match self.0.map.get(hand) { Some(spec) => spec, None => { @@ -520,14 +548,14 @@ impl HumArmorHandSpec { &self.0.default }; - let mut hand_segment = color_segment( + let mut hand_segment = color_spec.color_segment( if flipped { graceful_load_mat_segment_flipped(&spec.left.vox_spec.0) } else { graceful_load_mat_segment(&spec.right.vox_spec.0) }, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -546,25 +574,27 @@ 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 { - self.mesh_hand(body, loadout, true, generate_mesh) + color_spec: &HumColorSpec, + hand: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_hand(body, color_spec, hand, true, generate_mesh) } pub fn mesh_right_hand( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_hand(body, loadout, false, generate_mesh) + color_spec: &HumColorSpec, + hand: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_hand(body, color_spec, hand, false, generate_mesh) } } // Belt @@ -577,14 +607,11 @@ impl HumArmorBeltSpec { pub fn mesh_belt( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Belt(belt), - .. - })) = loadout.belt.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + belt: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(belt) = belt { match self.0.map.get(belt) { Some(spec) => spec, None => { @@ -596,10 +623,10 @@ impl HumArmorBeltSpec { &self.0.default }; - let mut belt_segment = color_segment( + let mut belt_segment = color_spec.color_segment( graceful_load_mat_segment(&spec.vox_spec.0), body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -608,7 +635,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 @@ -621,14 +648,11 @@ impl HumArmorBackSpec { pub fn mesh_back( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Back(back), - .. - })) = loadout.back.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + back: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(back) = back { match self.0.map.get(back) { Some(spec) => spec, None => { @@ -640,10 +664,10 @@ impl HumArmorBackSpec { &self.0.default }; - let mut back_segment = color_segment( + let mut back_segment = color_spec.color_segment( graceful_load_mat_segment(&spec.vox_spec.0), body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); if let Some(color) = spec.color { @@ -651,7 +675,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 @@ -664,14 +688,11 @@ impl HumArmorPantsSpec { pub fn mesh_pants( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Pants(pants), - .. - })) = loadout.pants.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + pants: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(pants) = pants { match self.0.map.get(pants) { Some(spec) => spec, None => { @@ -684,10 +705,10 @@ impl HumArmorPantsSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -707,7 +728,7 @@ impl HumArmorPantsSpec { .unify() .0; - generate_mesh(&pants, Vec3::from(spec.vox_spec.1)) + generate_mesh(pants, Vec3::from(spec.vox_spec.1)) } } // Foot @@ -720,15 +741,12 @@ impl HumArmorFootSpec { fn mesh_foot( &self, body: &Body, - loadout: &Loadout, + color_spec: &HumColorSpec, + foot: Option<&str>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Foot(foot), - .. - })) = loadout.foot.as_ref().map(|i| &i.kind) - { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(foot) = foot { match self.0.map.get(foot) { Some(spec) => spec, None => { @@ -740,14 +758,14 @@ impl HumArmorFootSpec { &self.0.default }; - let mut foot_segment = color_segment( + let mut foot_segment = color_spec.color_segment( if flipped { graceful_load_mat_segment_flipped(&spec.vox_spec.0) } else { graceful_load_mat_segment(&spec.vox_spec.0) }, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); @@ -756,25 +774,27 @@ 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 { - self.mesh_foot(body, loadout, true, generate_mesh) + color_spec: &HumColorSpec, + foot: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_foot(body, color_spec, foot, true, generate_mesh) } pub fn mesh_right_foot( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - self.mesh_foot(body, loadout, false, generate_mesh) + color_spec: &HumColorSpec, + foot: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + self.mesh_foot(body, color_spec, foot, false, generate_mesh) } } @@ -786,14 +806,14 @@ impl HumMainWeaponSpec { pub fn mesh_main_weapon( &self, - item_kind: Option<&ItemKind>, + tool_kind: Option<&ToolKind>, flipped: bool, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let tool_kind = if let Some(ItemKind::Tool(Tool { kind, .. })) = item_kind { + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let tool_kind = if let Some(kind) = tool_kind { kind } else { - return Mesh::new(); + return (Mesh::new(), (anim::vek::Aabb::default(), 0..0)); }; let spec = match self.0.get(tool_kind) { @@ -822,7 +842,7 @@ impl HumMainWeaponSpec { spec.vox_spec.1[2], ); - generate_mesh(&tool_kind_segment, offset) + generate_mesh(tool_kind_segment, offset) } } @@ -835,12 +855,11 @@ impl HumArmorLanternSpec { pub fn mesh_lantern( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Lantern(Lantern { kind, .. })) = - loadout.lantern.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + lantern: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(kind) = lantern { match self.0.map.get(kind) { Some(spec) => spec, None => { @@ -852,10 +871,10 @@ impl HumArmorLanternSpec { &self.0.default }; - let mut lantern_segment = color_segment( + let mut lantern_segment = color_spec.color_segment( graceful_load_mat_segment(&spec.vox_spec.0), body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ); if let Some(color) = spec.color { @@ -864,7 +883,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 { @@ -876,14 +895,11 @@ impl HumArmorHeadSpec { pub fn mesh_head( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Head(head), - .. - })) = loadout.head.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + head: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(head) = head { match self.0.map.get(head) { Some(spec) => spec, None => { @@ -896,10 +912,10 @@ impl HumArmorHeadSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -919,7 +935,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 { @@ -931,14 +947,11 @@ impl HumArmorTabardSpec { pub fn mesh_tabard( &self, body: &Body, - loadout: &Loadout, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, - ) -> Mesh { - let spec = if let Some(ItemKind::Armor(Armor { - kind: ArmorKind::Tabard(tabard), - .. - })) = loadout.tabard.as_ref().map(|i| &i.kind) - { + color_spec: &HumColorSpec, + tabard: Option<&str>, + generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes, + ) -> BoneMeshes { + let spec = if let Some(tabard) = tabard { match self.0.map.get(tabard) { Some(spec) => spec, None => { @@ -951,10 +964,10 @@ impl HumArmorTabardSpec { }; let color = |mat_segment| { - color_segment( + color_spec.color_segment( mat_segment, body.species.skin_color(body.skin), - body.species.hair_color(body.hair_color), + color_spec.hair_color(body.species, body.hair_color), body.species.eye_color(body.eye_color), ) }; @@ -974,13 +987,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), @@ -988,9 +999,7 @@ pub fn mesh_glider( ) } -pub fn mesh_hold( - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { +pub fn mesh_hold(generate_mesh: impl FnOnce(Segment, Vec3) -> BoneMeshes) -> BoneMeshes { load_mesh( "weapon.projectile.simple-arrow", Vec3::new(-0.5, -6.0, -1.5), @@ -1056,8 +1065,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 => { @@ -1070,15 +1079,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 => { @@ -1091,15 +1100,15 @@ 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)) } pub fn mesh_tail( &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 => { @@ -1112,7 +1121,7 @@ impl QuadrupedSmallCentralSpec { }; 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)) } } @@ -1126,8 +1135,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 => { @@ -1140,15 +1149,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_fr( &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 => { @@ -1161,15 +1170,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_bl( &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 => { @@ -1182,15 +1191,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_br( &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 => { @@ -1203,7 +1212,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)) } } @@ -1272,8 +1281,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 => { @@ -1286,15 +1295,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 => { @@ -1307,15 +1316,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 => { @@ -1328,15 +1337,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 => { @@ -1349,15 +1358,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_front( &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 => { @@ -1370,15 +1379,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.torso_front.central.0); - generate_mesh(¢ral, Vec3::from(spec.torso_front.offset)) + generate_mesh(central, Vec3::from(spec.torso_front.offset)) } pub fn mesh_torso_back( &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 => { @@ -1391,15 +1400,15 @@ impl QuadrupedMediumCentralSpec { }; let central = graceful_load_segment(&spec.torso_back.central.0); - generate_mesh(¢ral, Vec3::from(spec.torso_back.offset)) + generate_mesh(central, Vec3::from(spec.torso_back.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 => { @@ -1412,7 +1421,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)) } } @@ -1426,8 +1435,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 => { @@ -1440,15 +1449,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_fl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_fl.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_fl.offset)) } pub fn mesh_leg_fr( &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 => { @@ -1461,15 +1470,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_fr.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_fr.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_fr.offset)) } pub fn mesh_leg_bl( &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 => { @@ -1482,15 +1491,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_bl.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_bl.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_bl.offset)) } pub fn mesh_leg_br( &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 => { @@ -1503,15 +1512,15 @@ impl QuadrupedMediumLateralSpec { }; let lateral = graceful_load_segment(&spec.leg_br.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.leg_br.offset)) + generate_mesh(lateral, Vec3::from(spec.leg_br.offset)) } pub fn mesh_foot_fl( &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 => { @@ -1524,15 +1533,15 @@ impl QuadrupedMediumLateralSpec { }; 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: 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 => { @@ -1545,15 +1554,15 @@ impl QuadrupedMediumLateralSpec { }; 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: 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 => { @@ -1566,15 +1575,15 @@ impl QuadrupedMediumLateralSpec { }; 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: 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 => { @@ -1587,7 +1596,7 @@ impl QuadrupedMediumLateralSpec { }; 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)) } } @@ -1649,8 +1658,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 => { @@ -1663,15 +1672,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 => { @@ -1684,15 +1693,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 => { @@ -1705,7 +1714,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 { @@ -1718,8 +1727,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 => { @@ -1732,15 +1741,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 => { @@ -1753,15 +1762,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 => { @@ -1774,15 +1783,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 => { @@ -1795,7 +1804,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)) } } //// @@ -1833,8 +1842,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 => { @@ -1847,15 +1856,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 => { @@ -1868,15 +1877,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 => { @@ -1889,15 +1898,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 => { @@ -1910,15 +1919,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 => { @@ -1931,14 +1940,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", @@ -1950,8 +1959,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", @@ -1963,8 +1972,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", @@ -1976,8 +1985,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", @@ -1989,8 +1998,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", @@ -2002,8 +2011,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", @@ -2079,8 +2088,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 => { @@ -2093,15 +2102,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 => { @@ -2114,15 +2123,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 => { @@ -2135,15 +2144,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 => { @@ -2156,15 +2165,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 => { @@ -2177,15 +2186,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 => { @@ -2198,15 +2207,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 => { @@ -2219,7 +2228,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 { @@ -2231,8 +2240,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 => { @@ -2245,15 +2254,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 => { @@ -2266,15 +2275,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 => { @@ -2287,15 +2296,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 => { @@ -2308,15 +2317,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 => { @@ -2329,15 +2338,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 => { @@ -2350,15 +2359,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 => { @@ -2371,15 +2380,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 => { @@ -2392,15 +2401,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", @@ -2412,8 +2421,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", @@ -2425,8 +2434,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", @@ -2438,8 +2447,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", @@ -2451,8 +2460,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", @@ -2464,8 +2473,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", @@ -2540,8 +2549,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 => { @@ -2554,15 +2563,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_jaw( &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 => { @@ -2575,15 +2584,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.jaw.center.0); - generate_mesh(¢er, Vec3::from(spec.jaw.offset)) + generate_mesh(center, Vec3::from(spec.jaw.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 => { @@ -2596,15 +2605,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 => { @@ -2617,15 +2626,15 @@ 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)) } pub fn mesh_tail( &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 => { @@ -2638,15 +2647,15 @@ impl BipedLargeCenterSpec { }; 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_main( &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 => { @@ -2659,15 +2668,15 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.main.center.0); - generate_mesh(¢er, Vec3::from(spec.main.offset)) + generate_mesh(center, Vec3::from(spec.main.offset)) } pub fn mesh_second( &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 => { @@ -2680,7 +2689,7 @@ impl BipedLargeCenterSpec { }; let center = graceful_load_segment(&spec.second.center.0); - generate_mesh(¢er, Vec3::from(spec.second.offset)) + generate_mesh(center, Vec3::from(spec.second.offset)) } } impl BipedLargeLateralSpec { @@ -2693,8 +2702,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 => { @@ -2707,15 +2716,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 => { @@ -2728,15 +2737,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 => { @@ -2749,15 +2758,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 => { @@ -2770,15 +2779,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 => { @@ -2791,15 +2800,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 => { @@ -2812,15 +2821,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 => { @@ -2833,15 +2842,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 => { @@ -2854,7 +2863,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)) } } //// @@ -2917,8 +2926,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 => { @@ -2931,15 +2940,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 => { @@ -2952,7 +2961,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 { @@ -2964,8 +2973,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 => { @@ -2978,15 +2987,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 => { @@ -2999,15 +3008,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 => { @@ -3020,15 +3029,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 => { @@ -3041,15 +3050,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 => { @@ -3062,15 +3071,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 => { @@ -3083,15 +3092,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 => { @@ -3104,15 +3113,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 => { @@ -3125,7 +3134,7 @@ 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)) } } @@ -3190,8 +3199,8 @@ impl QuadrupedLowCentralSpec { &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3204,15 +3213,15 @@ impl QuadrupedLowCentralSpec { }; 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: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3225,15 +3234,15 @@ impl QuadrupedLowCentralSpec { }; 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: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3246,15 +3255,15 @@ impl QuadrupedLowCentralSpec { }; 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_chest( &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3267,15 +3276,15 @@ impl QuadrupedLowCentralSpec { }; 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)) } pub fn mesh_tail_rear( &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3288,15 +3297,15 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.tail_rear.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail_rear.offset)) + generate_mesh(central, Vec3::from(spec.tail_rear.offset)) } pub fn mesh_tail_front( &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3309,7 +3318,7 @@ impl QuadrupedLowCentralSpec { }; let central = graceful_load_segment(&spec.tail_front.central.0); - generate_mesh(¢ral, Vec3::from(spec.tail_front.offset)) + generate_mesh(central, Vec3::from(spec.tail_front.offset)) } } @@ -3323,8 +3332,8 @@ impl QuadrupedLowLateralSpec { &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3337,15 +3346,15 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.front_left.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.front_left.offset)) + generate_mesh(lateral, Vec3::from(spec.front_left.offset)) } pub fn mesh_foot_fr( &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3358,15 +3367,15 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.front_right.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.front_right.offset)) + generate_mesh(lateral, Vec3::from(spec.front_right.offset)) } pub fn mesh_foot_bl( &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3379,15 +3388,15 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.back_left.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.back_left.offset)) + generate_mesh(lateral, Vec3::from(spec.back_left.offset)) } pub fn mesh_foot_br( &self, species: QLSpecies, body_type: QLBodyType, - 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 => { @@ -3400,16 +3409,16 @@ impl QuadrupedLowLateralSpec { }; let lateral = graceful_load_segment(&spec.back_right.lateral.0); - generate_mesh(&lateral, Vec3::from(spec.back_right.offset)) + generate_mesh(lateral, Vec3::from(spec.back_right.offset)) } } /// pub fn mesh_object( - obj: object::Body, - generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, -) -> Mesh { + obj: &object::Body, + 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 8197620eb1..f45f660c61 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -6,10 +6,14 @@ pub use load::load_mesh; // TODO: Don't make this public. use crate::{ ecs::comp::Interpolated, - render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow}, + mesh::greedy::GreedyMesh, + render::{ + ColLightFmt, Consts, FigureBoneData, FigureLocals, FigureModel, GlobalModel, Mesh, + RenderError, Renderer, ShadowPipeline, TerrainPipeline, Texture, + }, scene::{ - camera::{Camera, CameraMode}, - SceneData, + camera::{Camera, CameraMode, Dependents}, + math, LodData, SceneData, }, }; use anim::{ @@ -30,29 +34,52 @@ use common::{ terrain::TerrainChunk, vol::RectRasterableVol, }; +use core::{ + borrow::Borrow, + convert::TryFrom, + hash::Hash, + ops::{Deref, DerefMut, Range}, +}; +use guillotiere::AtlasAllocator; use hashbrown::HashMap; -use specs::{Entity as EcsEntity, Join, WorldExt}; -use tracing::trace; +use specs::{Entity as EcsEntity, Join, LazyUpdate, WorldExt}; use treeculler::{BVol, BoundingSphere}; -use vek::*; 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, - quadruped_low_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, +/// camera data, figure LOD render distance. +pub type CameraData<'a> = (&'a Camera, f32); + +/// Enough data to render a figure model. +pub type FigureModelRef<'a> = ( + &'a Consts, + &'a Consts, + &'a FigureModel, + &'a Texture, +); + +/// An entry holding enough information to draw or destroy a figure in a +/// particular cache. +pub struct FigureModelEntry { + /// The estimated bounds of this figure, in voxels. This may not be very + /// useful yet. + _bounds: math::Aabb, + /// Hypothetical texture atlas allocation data for the current figure. + /// Will be useful if we decide to use a packed texture atlas for figures + /// like we do for terrain. + allocation: guillotiere::Allocation, + /// Texture used to store color/light information for this figure entry. + /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different + * LOD levels. */ + col_lights: Texture, + /// Models stored in this figure entry; there may be several for one figure, + /// because of LOD models. + pub models: [FigureModel; N], +} + +struct FigureMgrStates { character_states: HashMap>, quadruped_small_states: HashMap>, quadruped_medium_states: HashMap>, @@ -68,22 +95,9 @@ pub struct FigureMgr { object_states: HashMap>, } -impl FigureMgr { - #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { +impl FigureMgrStates { + pub fn default() -> Self { Self { - model_cache: FigureModelCache::new(), - critter_model_cache: FigureModelCache::new(), - quadruped_small_model_cache: FigureModelCache::new(), - quadruped_medium_model_cache: FigureModelCache::new(), - quadruped_low_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(), @@ -100,22 +114,251 @@ impl FigureMgr { } } - 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.quadruped_low_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); + 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::QuadrupedLow(_) => self + .quadruped_low_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), + } } - #[allow(clippy::redundant_pattern_matching)] // TODO: Pending review in #587 + 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::QuadrupedLow(_) => self.quadruped_low_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(&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.quadruped_low_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.quadruped_low_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 + .quadruped_low_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, + quadruped_low_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(), + quadruped_low_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::default(), + } + } + + pub fn col_lights(&self) -> &FigureColLights { &self.col_lights } + + pub fn clean(&mut self, tick: u64) { + 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.quadruped_low_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); + } + + #[allow(clippy::redundant_pattern_matching)] + // TODO: Pending review in #587 pub fn update_lighting(&mut self, scene_data: &SceneData) { let ecs = scene_data.state.ecs(); for (entity, light_emitter) in (&ecs.entities(), &ecs.read_storage::()).join() @@ -124,7 +367,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: vek::Vec3::zero(), col: light_emitter.col, strength: 0.0, }; @@ -132,9 +375,10 @@ impl FigureMgr { } } let dt = ecs.fetch::().0; + let updater = ecs.read_resource::(); for (entity, waypoint, light_emitter_opt, light_anim) in ( &ecs.entities(), - ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), &mut ecs.write_storage::(), ) @@ -144,7 +388,7 @@ impl FigureMgr { if let Some(emitter) = light_emitter_opt { ( emitter.col, - if emitter.strength.is_finite() { + if emitter.strength.is_normal() { emitter.strength } else { 0.0 @@ -153,15 +397,15 @@ impl FigureMgr { emitter.animated, ) } else { - (Rgb::zero(), 0.0, 0.0, true) + (vek::Rgb::zero(), 0.0, 0.0, true) }; if let Some(_) = waypoint { - light_anim.offset = Vec3::unit_z() * 0.5; + light_anim.offset = vek::Vec3::unit_z() * 5.0; } - if let Some(state) = self.character_states.get(&entity) { - light_anim.offset = state.lantern_offset; + if let Some(state) = self.states.character_states.get(&entity) { + light_anim.offset = vek::Vec3::from(state.lantern_offset); } - if !light_anim.strength.is_finite() { + if !light_anim.strength.is_normal() { light_anim.strength = 0.0; } if animated { @@ -175,23 +419,104 @@ impl FigureMgr { light_anim.strength = target_strength; light_anim.col = target_col; } + // NOTE: We add `LIGHT_EPSILON` because if we wait for numbers to become + // equal to target (or even within a subnormal), it will take a minimum + // of 30 seconds for a light to fully turn off (for initial + // strength ≥ 1), which prevents optimizations (particularly those that + // can kick in with zero lights). + const LIGHT_EPSILON: f32 = 0.0001; + if (light_anim.strength - target_strength).abs() < LIGHT_EPSILON { + light_anim.strength = target_strength; + if light_anim.strength == 0.0 { + updater.remove::(entity); + } + } } } - #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 - pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData, camera: &Camera) { + #[allow(clippy::or_fun_call)] + // TODO: Pending review in #587 + pub fn maintain( + &mut self, + renderer: &mut Renderer, + scene_data: &SceneData, + // Visible chunk data. + visible_psr_bounds: math::Aabr, + camera: &Camera, + ) -> anim::vek::Aabb { let state = scene_data.state; let time = state.get_time(); let tick = scene_data.tick; let ecs = state.ecs(); let view_distance = scene_data.view_distance; let dt = state.get_delta_time(); + let dt_lerp = (15.0 * dt).min(1.0); let frustum = camera.frustum(); + + // Sun shadows--find the bounding box of the shadow map plane (i.e. the bounds + // of the image rendered from the light). If the position projected + // with the ray_mat matrix is valid, and shadows are otherwise enabled, + // we mark can_shadow. + let can_shadow_sun = { + let ray_direction = scene_data.get_sun_dir(); + let is_daylight = ray_direction.z < 0.0/*0.6*/; + // Are shadows enabled at all? + let can_shadow_sun = renderer.render_mode().shadow.is_map() && is_daylight; + let Dependents { + proj_mat: _, + view_mat: _, + cam_pos, + .. + } = camera.dependents(); + let cam_pos = math::Vec3::from(cam_pos); + let ray_direction = math::Vec3::from(ray_direction); + + // Transform (semi) world space to light space. + let ray_mat: math::Mat4 = + math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, math::Vec3::up()); + let focus_off = math::Vec3::from(camera.get_focus_pos().map(f32::trunc)); + let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off); + + let collides_with_aabr = |a: math::Aabr, b: math::Aabr| { + let min = math::Vec4::new(a.min.x, a.min.y, b.min.x, b.min.y); + let max = math::Vec4::new(b.max.x, b.max.y, a.max.x, a.max.y); + min.partial_cmple_simd(max).reduce_and() + }; + move |pos: (anim::vek::Vec3,), radius: f32| { + // Short circuit when there are no shadows to cast. + if !can_shadow_sun { + return false; + } + // First project center onto shadow map. + let center = (ray_mat * math::Vec4::new(pos.0.x, pos.0.y, pos.0.z, 1.0)).xy(); + // Then, create an approximate bounding box (± radius). + let figure_box = math::Aabr { + min: center - radius, + max: center + radius, + }; + // Quick intersection test for membership in the PSC (potential shader caster) + // list. + collides_with_aabr(figure_box, visible_psr_bounds) + } + }; + // Get player position. let player_pos = ecs .read_storage::() .get(scene_data.player_entity) - .map_or(Vec3::zero(), |pos| pos.0); + .map_or(anim::vek::Vec3::zero(), |pos| anim::vek::Vec3::from(pos.0)); + let visible_aabb = anim::vek::Aabb { + min: player_pos - 2.0, + max: player_pos + 2.0, + }; + let camera_mode = camera.get_mode(); + let character_state_storage = state.read_storage::(); + let character_state = character_state_storage.get(scene_data.player_entity); + + let focus_pos = anim::vek::Vec3::::from(camera.get_focus_pos()); + + let mut update_buf = [Default::default(); anim::MAX_BONE_COUNT]; + for ( i, ( @@ -225,6 +550,27 @@ impl FigureMgr { .join() .enumerate() { + let vel = (anim::vek::Vec3::::from(vel.0),); + let is_player = scene_data.player_entity == entity; + let player_camera_mode = if is_player { + camera_mode + } else { + CameraMode::default() + }; + let player_character_state = if is_player { character_state } else { None }; + + let (pos, ori) = interpolated + .map(|i| { + ( + (anim::vek::Vec3::from(i.pos),), + anim::vek::Vec3::from(*i.ori), + ) + }) + .unwrap_or(( + (anim::vek::Vec3::::from(pos.0),), + anim::vek::Vec3::::unit_y(), + )); + // 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,8 +579,9 @@ impl FigureMgr { // TODO: Investigate passing the velocity into the shader so we can at least // interpolate motion const MIN_PERFECT_RATE_DIST: f32 = 50.0; + if (i as u64 + tick) - % (1 + ((pos.0.distance_squared(camera.get_focus_pos()).powf(0.25) + % (1 + ((pos.0.distance_squared(focus_pos).powf(0.25) - MIN_PERFECT_RATE_DIST.powf(0.5)) .max(0.0) / 3.0) as u64) @@ -243,289 +590,75 @@ 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())); + // Check whether we could have been shadowing last frame. + let mut state = self.states.get_mut(body, &entity); + let can_shadow_prev = state + .as_mut() + .map(|state| state.can_shadow_sun()) + .unwrap_or(false); // Don't process figures outside the vd - let vd_frac = Vec2::from(pos.0 - player_pos) - .map2(TerrainChunk::RECT_SIZE, |d: f32, sz| { - d.abs() as f32 / sz as f32 - }) + let vd_frac = anim::vek::Vec2::from(pos.0 - player_pos) + .map2( + anim::vek::Vec2::::from(TerrainChunk::RECT_SIZE), + |d: f32, sz| d.abs() as f32 / sz as f32, + ) .magnitude() / 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::QuadrupedLow(_) => { - self.quadruped_low_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::QuadrupedLow(_) => { - self.quadruped_low_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); - }, - } - 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::QuadrupedLow(_) => self - .quadruped_low_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::QuadrupedLow(_) => { - self.quadruped_low_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 - }); - }, + state.as_mut().map(|state| state.visible = false); + // Keep processing if this might be a shadow caster. + if !can_shadow_prev { + continue; } } + // 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). Currently, we don't do this before the + // update cull, so it's possible that faraway figures will not + // shadow correctly until their next update. For now, we treat this + // as an acceptable tradeoff. + let radius = scale.unwrap_or(&Scale(1.0)).0 * 2.0; + let (in_frustum, lpindex) = if let Some(mut meta) = state { + 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; + if in_frustum { + /* // Update visible bounds. + visible_aabb.expand_to_contain(Aabb { + min: pos.0 - radius, + max: pos.0 + radius, + }); */ + } else { + // Check whether we can shadow. + meta.can_shadow_sun = can_shadow_sun(pos, radius); + } + (in_frustum, lpindex) + } else { + (true, 0) + }; + // Change in health as color! let col = stats .map(|s| { - Rgba::broadcast(1.0) - + Rgba::new(2.0, 2.0, 2., 0.00).map(|c| { + vek::Rgba::broadcast(1.0) + + vek::Rgba::new(2.0, 2.0, 2., 0.00).map(|c| { (c / (1.0 + DAMAGE_FADE_COEFFICIENT * s.health.last_change.0)) as f32 }) }) - .unwrap_or(Rgba::broadcast(1.0)) + .unwrap_or(vek::Rgba::broadcast(1.0)) // Highlight targeted collectible entities * if item.is_some() && scene_data.target_entity.map_or(false, |e| e == entity) { - Rgba::new(2.0, 2.0, 2.0, 1.0) + vek::Rgba::new(2.0, 2.0, 2.0, 1.0) } else { - Rgba::one() + vek::Rgba::one() }; let scale = scale.map(|s| s.0).unwrap_or(1.0); @@ -553,22 +686,23 @@ 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, + player_camera_mode, + player_character_state, + ); let state = self + .states .character_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, CharacterSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), _ => continue, @@ -585,7 +719,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -598,7 +732,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::character::RunAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -614,7 +748,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::character::JumpAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -628,7 +762,7 @@ impl FigureMgr { ), // Swim (_, _, true) => anim::character::SwimAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind.clone(), second_tool_kind.clone(), @@ -722,7 +856,7 @@ impl FigureMgr { }, CharacterState::Sneak { .. } => { anim::character::SneakAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, vel.0, ori, state.last_ori, time), state.state_time, &mut state_animation_rate, @@ -796,7 +930,7 @@ impl FigureMgr { }, CharacterState::BasicBlock { .. } => { anim::character::BlockIdleAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, time), state.state_time, &mut state_animation_rate, @@ -859,7 +993,7 @@ impl FigureMgr { }, CharacterState::Climb { .. } => { anim::character::ClimbAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, vel.0, ori, time), state.state_time, &mut state_animation_rate, @@ -868,7 +1002,7 @@ impl FigureMgr { }, CharacterState::Sit { .. } => { anim::character::SitAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, time), state.state_time, &mut state_animation_rate, @@ -877,7 +1011,7 @@ impl FigureMgr { }, CharacterState::GlideWield { .. } => { anim::character::GlideWieldAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), ( active_tool_kind, second_tool_kind, @@ -893,7 +1027,7 @@ impl FigureMgr { }, CharacterState::Dance { .. } => { anim::character::DanceAnimation::update_skeleton( - &CharacterSkeleton::new(), + &CharacterSkeleton::default(), (active_tool_kind, second_tool_kind, time), state.state_time, &mut state_animation_rate, @@ -903,7 +1037,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -912,29 +1046,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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; + player_camera_mode, + player_character_state, + ); let state = self + .states .quadruped_small_states .entry(entity) .or_insert_with(|| { - FigureState::new(renderer, QuadrupedSmallSkeleton::new()) + FigureState::new(renderer, QuadrupedSmallSkeleton::default()) }); let (character, last_character) = match (character, last_character) { @@ -954,7 +1091,7 @@ impl FigureMgr { // Standing (true, false, false) => { anim::quadruped_small::IdleAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -964,7 +1101,7 @@ impl FigureMgr { // Running (true, true, false) => { anim::quadruped_small::RunAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -973,14 +1110,14 @@ impl FigureMgr { }, // In air (false, _, false) => anim::quadruped_small::JumpAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::quadruped_small::IdleAnimation::update_skeleton( - &QuadrupedSmallSkeleton::new(), + &QuadrupedSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1001,7 +1138,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1010,29 +1147,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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; + player_camera_mode, + player_character_state, + ); let state = self + .states .quadruped_medium_states .entry(entity) .or_insert_with(|| { - FigureState::new(renderer, QuadrupedMediumSkeleton::new()) + FigureState::new(renderer, QuadrupedMediumSkeleton::default()) }); let (character, last_character) = match (character, last_character) { @@ -1052,7 +1192,7 @@ impl FigureMgr { // Standing (true, false, false) => { anim::quadruped_medium::IdleAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1061,7 +1201,7 @@ impl FigureMgr { }, // Running (true, true, _) => anim::quadruped_medium::RunAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -1070,7 +1210,7 @@ impl FigureMgr { // In air (false, _, false) => { anim::quadruped_medium::JumpAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1078,7 +1218,7 @@ impl FigureMgr { ) }, _ => anim::quadruped_medium::IdleAnimation::update_skeleton( - &QuadrupedMediumSkeleton::new(), + &QuadrupedMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1099,7 +1239,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1108,28 +1248,33 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::QuadrupedLow(_) => { - let skeleton_attr = &self - .quadruped_low_model_cache - .get_or_create_model( + let (model, skeleton_attr) = + self.quadruped_low_model_cache.get_or_create_model( renderer, + &mut self.col_lights, *body, loadout, tick, - CameraMode::default(), - None, - ) - .1; + player_camera_mode, + player_character_state, + ); let state = self + .states .quadruped_low_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, QuadrupedLowSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, QuadrupedLowSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1148,7 +1293,7 @@ impl FigureMgr { // Standing (true, false, false) => { anim::quadruped_low::IdleAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1157,7 +1302,7 @@ impl FigureMgr { }, // Running (true, true, _) => anim::quadruped_low::RunAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -1165,14 +1310,14 @@ impl FigureMgr { ), // In air (false, _, false) => anim::quadruped_low::JumpAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::quadruped_low::IdleAnimation::update_skeleton( - &QuadrupedLowSkeleton::new(), + &QuadrupedLowSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1193,7 +1338,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1202,28 +1347,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); let state = self + .states .bird_medium_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, BirdMediumSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, BirdMediumSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1241,7 +1390,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1249,7 +1398,7 @@ impl FigureMgr { ), // Running (true, true, _) => anim::bird_medium::RunAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1257,14 +1406,14 @@ impl FigureMgr { ), // In air (false, _, false) => anim::bird_medium::FlyAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::bird_medium::IdleAnimation::update_skeleton( - &BirdMediumSkeleton::new(), + &BirdMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1285,7 +1434,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1294,28 +1443,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); let state = self + .states .fish_medium_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, FishMediumSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, FishMediumSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1333,7 +1486,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton( - &FishMediumSkeleton::new(), + &FishMediumSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1341,7 +1494,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::fish_medium::RunAnimation::update_skeleton( - &FishMediumSkeleton::new(), + &FishMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1349,7 +1502,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::fish_medium::JumpAnimation::update_skeleton( - &FishMediumSkeleton::new(), + &FishMediumSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1357,10 +1510,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::fish_medium::IdleAnimation::update_skeleton( + &FishMediumSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1369,28 +1528,29 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); - let state = self - .dragon_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, DragonSkeleton::new())); + let state = + self.states.dragon_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, DragonSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1408,7 +1568,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::dragon::IdleAnimation::update_skeleton( - &DragonSkeleton::new(), + &DragonSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1416,7 +1576,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::dragon::RunAnimation::update_skeleton( - &DragonSkeleton::new(), + &DragonSkeleton::default(), (vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel), state.state_time, &mut state_animation_rate, @@ -1424,17 +1584,23 @@ impl FigureMgr { ), // In air (false, _, false) => anim::dragon::FlyAnimation::update_skeleton( - &DragonSkeleton::new(), + &DragonSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, skeleton_attr, ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::dragon::IdleAnimation::update_skeleton( + &DragonSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1443,28 +1609,29 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); - let state = self - .critter_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, CritterSkeleton::new())); + let state = + self.states.critter_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, CritterSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1482,7 +1649,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::critter::IdleAnimation::update_skeleton( - &CritterSkeleton::new(), + &CritterSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1490,7 +1657,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::critter::RunAnimation::update_skeleton( - &CritterSkeleton::new(), + &CritterSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1498,7 +1665,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::critter::JumpAnimation::update_skeleton( - &CritterSkeleton::new(), + &CritterSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1506,10 +1673,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::critter::IdleAnimation::update_skeleton( + &CritterSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1518,28 +1691,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); let state = self + .states .bird_small_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, BirdSmallSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, BirdSmallSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1557,7 +1734,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::bird_small::IdleAnimation::update_skeleton( - &BirdSmallSkeleton::new(), + &BirdSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1565,7 +1742,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::bird_small::RunAnimation::update_skeleton( - &BirdSmallSkeleton::new(), + &BirdSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1573,7 +1750,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::bird_small::JumpAnimation::update_skeleton( - &BirdSmallSkeleton::new(), + &BirdSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1581,10 +1758,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::bird_small::IdleAnimation::update_skeleton( + &BirdSmallSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1593,28 +1776,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); let state = self + .states .fish_small_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, FishSmallSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, FishSmallSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1632,7 +1819,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::fish_small::IdleAnimation::update_skeleton( - &FishSmallSkeleton::new(), + &FishSmallSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1640,7 +1827,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::fish_small::RunAnimation::update_skeleton( - &FishSmallSkeleton::new(), + &FishSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1648,7 +1835,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::fish_small::JumpAnimation::update_skeleton( - &FishSmallSkeleton::new(), + &FishSmallSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1656,10 +1843,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::fish_small::IdleAnimation::update_skeleton( + &FishSmallSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1668,28 +1861,32 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); let state = self + .states .biped_large_states .entry(entity) - .or_insert_with(|| FigureState::new(renderer, BipedLargeSkeleton::new())); + .or_insert_with(|| { + FigureState::new(renderer, BipedLargeSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1707,7 +1904,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::biped_large::IdleAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1715,7 +1912,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::biped_large::RunAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1723,14 +1920,14 @@ impl FigureMgr { ), // In air (false, _, false) => anim::biped_large::JumpAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), time, state.state_time, &mut state_animation_rate, skeleton_attr, ), _ => anim::biped_large::IdleAnimation::update_skeleton( - &BipedLargeSkeleton::new(), + &BipedLargeSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1760,7 +1957,7 @@ impl FigureMgr { _ => target_base, }; - state.skeleton.interpolate(&target_bones, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp); state.update( renderer, pos.0, @@ -1769,28 +1966,29 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, 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, + player_camera_mode, + player_character_state, + ); - let state = self - .golem_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, GolemSkeleton::new())); + let state = + self.states.golem_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, GolemSkeleton::default()) + }); let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), @@ -1808,7 +2006,7 @@ impl FigureMgr { ) { // Standing (true, false, false) => anim::golem::IdleAnimation::update_skeleton( - &GolemSkeleton::new(), + &GolemSkeleton::default(), time, state.state_time, &mut state_animation_rate, @@ -1816,7 +2014,7 @@ impl FigureMgr { ), // Running (true, true, false) => anim::golem::RunAnimation::update_skeleton( - &GolemSkeleton::new(), + &GolemSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1824,7 +2022,7 @@ impl FigureMgr { ), // In air (false, _, false) => anim::golem::JumpAnimation::update_skeleton( - &GolemSkeleton::new(), + &GolemSkeleton::default(), (vel.0.magnitude(), time), state.state_time, &mut state_animation_rate, @@ -1832,10 +2030,16 @@ impl FigureMgr { ), // TODO! - _ => state.skeleton_mut().clone(), + _ => anim::golem::IdleAnimation::update_skeleton( + &GolemSkeleton::default(), + time, + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ), }; - state.skeleton.interpolate(&target_base, dt); + state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_base, dt); state.update( renderer, pos.0, @@ -1844,18 +2048,30 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, - true, + in_frustum, is_player, + camera, + &mut update_buf, ); }, Body::Object(_) => { - let state = self - .object_states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, ObjectSkeleton::new())); + let (model, _) = &self.model_cache.get_or_create_model( + renderer, + &mut self.col_lights, + *body, + loadout, + tick, + player_camera_mode, + player_character_state, + ); + + let state = + self.states.object_states.entry(entity).or_insert_with(|| { + FigureState::new(renderer, ObjectSkeleton::default()) + }); - state.skeleton = state.skeleton_mut().clone(); state.update( renderer, pos.0, @@ -1864,9 +2080,12 @@ impl FigureMgr { col, dt, state_animation_rate, + &model, lpindex, true, is_player, + camera, + &mut update_buf, ); }, } @@ -1876,32 +2095,60 @@ 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.quadruped_low_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, + global: &GlobalModel, + (is_daylight, _light_data): super::LightData, + (camera, figure_lod_render_distance): CameraData, + ) { + let ecs = state.ecs(); + + if is_daylight && renderer.render_mode().shadow.is_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.can_shadow_sun(), + ) { + renderer.render_figure_shadow_directed( + model, + global, + locals, + bone_consts, + &global.shadow_mats, + ); + } + }); + } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 @@ -1911,11 +2158,9 @@ impl FigureMgr { state: &State, player_entity: EcsEntity, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, - camera: &Camera, - figure_lod_render_distance: f32, + global: &GlobalModel, + lod: &LodData, + (camera, figure_lod_render_distance): CameraData, ) { let ecs = state.ecs(); @@ -1938,12 +2183,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, camera, character_state, entity, @@ -1952,7 +2194,10 @@ impl FigureMgr { false, pos.0, figure_lod_render_distance, - ); + |state| state.visible(), + ) { + renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod); + } } } } @@ -1964,11 +2209,9 @@ impl FigureMgr { state: &State, player_entity: EcsEntity, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, - camera: &Camera, - figure_lod_render_distance: f32, + global: &GlobalModel, + lod: &LodData, + (camera, figure_lod_render_distance): CameraData, ) { let ecs = state.ecs(); @@ -1989,12 +2232,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, camera, character_state, player_entity, @@ -2003,35 +2243,47 @@ impl FigureMgr { true, pos.0, figure_lod_render_distance, - ); + |state| state.visible(), + ) { + renderer.render_player(model, &col_lights, global, locals, bone_consts, lod); + renderer.render_player_shadow( + model, + &col_lights, + global, + bone_consts, + lod, + &global.shadow_mats, + ); + } } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 - fn render_figure( + fn get_model_for_render( &mut self, renderer: &mut Renderer, tick: u64, - globals: &Consts, - lights: &Consts, - shadows: &Consts, camera: &Camera, character_state: Option<&CharacterState>, entity: EcsEntity, body: &Body, loadout: Option<&Loadout>, is_player: bool, - pos: Vec3, + pos: vek::Vec3, figure_lod_render_distance: f32, - ) { + filter_state: impl Fn(&FigureStateMeta) -> bool, + ) -> Option { 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, @@ -2044,31 +2296,276 @@ impl FigureMgr { fish_small_model_cache, biped_large_model_cache, golem_model_cache, - character_states, - quadruped_small_states, - quadruped_medium_states, - quadruped_low_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, + quadruped_low_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; - if let Some((locals, bone_consts, model)) = match body { + let col_lights = &mut *col_lights_; + if let Some((locals, bone_consts, model_entry)) = 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, + player_camera_mode, + character_state, + ) + .0, + ) + }), + 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::QuadrupedLow(_) => quadruped_low_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &quadruped_low_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) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &bird_medium_model_cache + .get_or_create_model( + renderer, + col_lights, + *body, + loadout, + tick, + player_camera_mode, + character_state, + ) + .0, + ) + }), + Body::FishMedium(_) => fish_medium_states + .get(&entity) + .filter(|state| filter_state(&*state)) + .map(move |state| { + ( + state.locals(), + state.bone_consts(), + &fish_medium_model_cache + .get_or_create_model( + renderer, + col_lights, + *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, @@ -2078,367 +2575,244 @@ 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::QuadrupedLow(_) => quadruped_low_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &quadruped_low_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| { - ( - state.locals(), - state.bone_consts(), - &bird_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .0, - ) - }), - Body::FishMedium(_) => fish_medium_states.get(&entity).map(|state| { - ( - state.locals(), - state.bone_consts(), - &fish_medium_model_cache - .get_or_create_model( - renderer, - *body, - loadout, - tick, - player_camera_mode, - character_state, - ) - .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, - ) - }), } { 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) - { - &model[2] - } else if pos.distance_squared(camera.get_focus_pos()) - > figure_mid_detail_distance.powf(2.0) - { - &model[1] + let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powf(2.0) { + &model_entry.models[2] + } else if pos.distance_squared(cam_pos) > figure_mid_detail_distance.powf(2.0) { + &model_entry.models[1] } else { - &model[0] + &model_entry.models[0] }; - if is_player { - renderer.render_player(model, globals, locals, bone_consts, lights, shadows); - renderer.render_player_shadow(model, globals, locals, bone_consts, lights, shadows); - } else { - renderer.render_figure(model, globals, locals, bone_consts, lights, shadows); - } + Some((locals, bone_consts, model, col_lights_.texture(model_entry))) } 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.quadruped_low_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 = 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 - .quadruped_low_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() + /// Find the correct texture for this model entry. + pub fn texture<'a, const N: usize>( + &'a self, + model: &'a FigureModelEntry, + ) -> &'a Texture { + /* &self.col_lights */ + &model.col_lights + } + + /// NOTE: Panics if the opaque model's length does not fit in a u32. + /// This is part of the function contract. + /// + /// NOTE: Panics if the vertex range bounds are not in range of the opaque + /// model stored in the BoneMeshes parameter. This is part of the + /// function contract. + pub fn create_figure<'a, const N: usize>( + &mut self, + renderer: &mut Renderer, + greedy: GreedyMesh<'a>, + (opaque, bounds): (Mesh, math::Aabb), + vertex_range: [Range; N], + ) -> Result, RenderError> { + 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 failure."); + let col_lights = ShadowPipeline::create_col_lights(renderer, (tex, tex_size))?; + let model_len = u32::try_from(opaque.vertices().len()) + .expect("The model size for this figure does not fit in a u32!"); + let model = renderer.create_model(&opaque)?; + + Ok(FigureModelEntry { + _bounds: bounds, + models: vertex_range.map(|range| { + assert!( + range.start <= range.end && range.end <= model_len, + "The provided vertex range for figure mesh {:?} does not fit in the model, \ + which is of size {:?}!", + range, + model_len + ); + FigureModel { + opaque: model.submodel(range), + } + }), + col_lights, + allocation, + }) + } + + fn make_atlas(renderer: &mut Renderer) -> Result { + 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 = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { + // TODO: Verify some good empirical constants. + small_size_threshold: 32, + large_size_threshold: 256, + ..guillotiere::AllocatorOptions::default() + }); + // TODO: Consider using a single texture atlas to store all figures, much like + // we do for terrain chunks. We previously avoided this due to + // perceived performance degradation for the figure use case, but with a + // smaller atlas size this may be less likely. + /* 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::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, + ), + )?; + Ok((atlas, texture)) */ + Ok(atlas) } } -pub struct FigureState { +pub struct FigureStateMeta { bone_consts: Consts, locals: Consts, - lantern_offset: Vec3, + lantern_offset: anim::vek::Vec3, state_time: f64, - skeleton: S, - last_ori: Vec3, + last_ori: anim::vek::Vec3, lpindex: u8, + can_shadow_sun: bool, visible: bool, - last_pos: Option>, - avg_vel: Vec3, + last_pos: Option>, + avg_vel: anim::vek::Vec3, +} + +impl FigureStateMeta { + pub fn visible(&self) -> bool { self.visible } + + pub fn can_shadow_sun(&self) -> bool { + // Either visible, or explicitly a shadow caster. + self.visible || self.can_shadow_sun + } +} + +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_mats, lantern_offset) = skeleton.compute_matrices(); - let bone_consts = figure_bone_data_from_anim(bone_mats); + let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; + let lantern_offset = + anim::compute_matrices(&skeleton, anim::vek::Mat4::identity(), &mut buf); + let bone_consts = figure_bone_data_from_anim(&buf); 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: anim::vek::Vec3::zero(), + lpindex: 0, + visible: false, + can_shadow_sun: false, + last_pos: None, + avg_vel: anim::vek::Vec3::zero(), + }, skeleton, - last_ori: Vec3::zero(), - lpindex: 0, - visible: false, - last_pos: None, - avg_vel: Vec3::zero(), } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 - pub fn update( + pub fn update( &mut self, renderer: &mut Renderer, - pos: Vec3, - ori: Vec3, + pos: anim::vek::Vec3, + ori: anim::vek::Vec3, scale: f32, - col: Rgba, + col: vek::Rgba, dt: f32, state_animation_rate: f32, - lpindex: u8, - visible: bool, + model: &FigureModelEntry, + _lpindex: u8, + _visible: bool, is_player: bool, + camera: &Camera, + buf: &mut [anim::FigureBoneData; anim::MAX_BONE_COUNT], ) { - self.visible = visible; - self.lpindex = lpindex; - // 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); + 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 = vek::Extent3::::from(model.bounds.half_size()).reduce_partial_max(); + let _bounds = BoundingSphere::new(pos.into_array(), scale * 0.8 * radius); */ + + self.last_ori = vek::Lerp::lerp(self.last_ori, ori, 15.0 * dt); self.state_time += (dt * state_animation_rate) as f64; - let mat = Mat4::::identity() - * Mat4::translation_3d(pos) - * 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 mat = anim::vek::Mat4::rotation_z(-ori.x.atan2(ori.y)) + * anim::vek::Mat4::rotation_x(ori.z.atan2(anim::vek::Vec2::from(ori).magnitude())) + * anim::vek::Mat4::scaling_3d(anim::vek::Vec3::from(0.8 * scale)); - let locals = FigureLocals::new(mat, col, is_player); + let atlas_offs = model.allocation.rectangle.min; + let locals = FigureLocals::new( + mat, + col, + pos, + vek::Vec2::new(atlas_offs.x, atlas_offs.y), + is_player, + ); renderer.update_consts(&mut self.locals, &[locals]).unwrap(); - let (new_bone_mats, lantern_offset) = self.skeleton.compute_matrices(); - let new_bone_consts = figure_bone_data_from_anim(new_bone_mats); + let lantern_offset = anim::compute_matrices(&self.skeleton, mat, buf); + + let new_bone_consts = figure_bone_data_from_anim(buf); renderer .update_consts( - &mut self.bone_consts, - &new_bone_consts[0..self.skeleton.bone_count()], + &mut self.meta.bone_consts, + &new_bone_consts[0..S::BONE_COUNT], ) .unwrap(); self.lantern_offset = lantern_offset; @@ -2457,23 +2831,8 @@ impl FigureState { pub fn skeleton_mut(&mut self) -> &mut S { &mut self.skeleton } } -fn figure_bone_data_from_anim(mats: [anim::FigureBoneData; 16]) -> [FigureBoneData; 16] { - [ - FigureBoneData::new(mats[0].0), - FigureBoneData::new(mats[1].0), - FigureBoneData::new(mats[2].0), - FigureBoneData::new(mats[3].0), - FigureBoneData::new(mats[4].0), - FigureBoneData::new(mats[5].0), - FigureBoneData::new(mats[6].0), - FigureBoneData::new(mats[7].0), - FigureBoneData::new(mats[8].0), - FigureBoneData::new(mats[9].0), - FigureBoneData::new(mats[10].0), - FigureBoneData::new(mats[11].0), - FigureBoneData::new(mats[12].0), - FigureBoneData::new(mats[13].0), - FigureBoneData::new(mats[14].0), - FigureBoneData::new(mats[15].0), - ] +fn figure_bone_data_from_anim( + mats: &[anim::FigureBoneData; anim::MAX_BONE_COUNT], +) -> &[FigureBoneData] { + gfx::memory::cast_slice(mats) } diff --git a/voxygen/src/scene/lod.rs b/voxygen/src/scene/lod.rs new file mode 100644 index 0000000000..f241d8fa0d --- /dev/null +++ b/voxygen/src/scene/lod.rs @@ -0,0 +1,97 @@ +use crate::{ + render::{ + pipelines::lod_terrain::{Locals, LodData, Vertex}, + Consts, GlobalModel, LodTerrainPipeline, Mesh, Model, Quad, Renderer, + }, + settings::Settings, +}; +use client::Client; +use common::{spiral::Spiral2d, util::srgba_to_linear}; +use vek::*; + +pub struct Lod { + model: Option<(u32, Model)>, + locals: Consts, + data: LodData, +} + +// TODO: Make constant when possible. +pub fn water_color() -> Rgba { + /* Rgba::new(0.2, 0.5, 1.0, 0.0) */ + srgba_to_linear(Rgba::new(0.0, 0.25, 0.5, 0.0)) +} + +impl Lod { + pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self { + Self { + model: None, + locals: renderer.create_consts(&[Locals::default()]).unwrap(), + data: LodData::new( + renderer, + client.world_map.1, + &client.lod_base, + &client.lod_alt, + &client.lod_horizon, + settings.graphics.lod_detail.max(100).min(2500), + water_color().into_array().into(), + ), + } + } + + pub fn get_data(&self) -> &LodData { &self.data } + + pub fn set_detail(&mut self, detail: u32) { + // Make sure the recorded detail is even. + self.data.tgt_detail = (detail - detail % 2).max(100).min(2500); + } + + pub fn maintain(&mut self, renderer: &mut Renderer) { + if self + .model + .as_ref() + .map(|(detail, _)| *detail != self.data.tgt_detail) + .unwrap_or(true) + { + self.model = Some(( + self.data.tgt_detail, + renderer + .create_model(&create_lod_terrain_mesh(self.data.tgt_detail)) + .unwrap(), + )); + } + } + + pub fn render(&self, renderer: &mut Renderer, global: &GlobalModel) { + if let Some((_, model)) = self.model.as_ref() { + renderer.render_lod_terrain(&model, global, &self.locals, &self.data); + } + } +} + +fn create_lod_terrain_mesh(detail: u32) -> Mesh { + // detail is even, so we choose odd detail (detail + 1) to create two even + // halves with an empty hole. + let detail = detail + 1; + Spiral2d::new() + .take((detail * detail) as usize) + .skip(1) + .map(|pos| { + let x = pos.x + detail as i32 / 2; + let y = pos.y + detail as i32 / 2; + + let transform = |x| (2.0 * x as f32) / detail as f32 - 1.0; + + Quad::new( + Vertex::new(Vec2::new(x, y).map(transform)), + Vertex::new(Vec2::new(x + 1, y).map(transform)), + Vertex::new(Vec2::new(x + 1, y + 1).map(transform)), + Vertex::new(Vec2::new(x, y + 1).map(transform)), + ) + .rotated_by(if (x > detail as i32 / 2) ^ (y > detail as i32 / 2) { + 0 + } else { + 1 + }) + }) + .collect() +} diff --git a/voxygen/src/scene/math.rs b/voxygen/src/scene/math.rs new file mode 100644 index 0000000000..3ebb5fdfad --- /dev/null +++ b/voxygen/src/scene/math.rs @@ -0,0 +1,390 @@ +use core::{iter, mem}; +use hashbrown::HashMap; +use num::traits::Float; +pub use vek::{geom::repr_simd::*, mat::repr_simd::column_major::Mat4, ops::*, vec::repr_simd::*}; +// pub use vek::{geom::repr_c::*, mat::repr_c::column_major::Mat4, ops::*, +// vec::repr_c::*}; + +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) -> [Vec4; 6] { + let zero = T::zero(); + let one = T::one(); + let bounds = bounds.map(|e| e.abs()); + [ + // bottom + Vec4::new(zero, -one, zero, -bounds.min.y), + // top + Vec4::new(zero, one, zero, -bounds.max.y), + // left + Vec4::new(-one, zero, zero, -bounds.min.x), + // right + Vec4::new(one, zero, zero, -bounds.max.x), + // near + Vec4::new(zero, zero, -one, -bounds.min.z), + // far + Vec4::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.iter_mut().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: Vec4) -> T { + norm_dist.dot(Vec4::from_point(point)) +} + +pub fn point_before_plane(point: Vec3, plane: Vec4) -> 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: Vec4, + intersection_points: &mut Vec>, +) -> bool { + if points.len() < 3 { + return false; + } + // NOTE: Guaranteed to succeed since points.len() > 3. + let mut current_point = points[points.len() - 1]; + let intersect_plane_edge = |a, b| { + let diff: Vec3<_> = b - a; + let t = plane.dot(Vec4::from_direction(diff)); + if t == T::zero() { + None + } else { + let t = -(plane.dot(Vec4::from_point(a)) / t); + if t < T::zero() || T::one() < t { + None + } else { + Some(diff * t + a) + } + } + }; + let last_is_outside = point_before_plane(current_point, plane); + let mut is_outside = last_is_outside; + 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 = mem::replace(&mut current_point, point); + let before_plane = point_before_plane(current_point, plane); + let prev_is_outside = mem::replace(&mut is_outside, before_plane); + 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); + } + } + }); + 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)) + }); + + if let Some((last_key, first)) = lines_iter.next() { + let lines = lines_iter.collect::>(); + if lines.len() < 2 { + // You need at least 3 sides for a polygon + return; + } + // 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 poly_iter = iter::successors(Some(first), |&cur| lines.get(&make_key(cur)).copied()); + 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: Vec4, + 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); + // 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() + }); + // 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); + planes.iter().for_each(|&plane| { + clip_object_by_plane(polys, plane, tolerance); + }); +} + +/// 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)> { + 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 })) + } + } +} + +pub fn intersection_line_aabb + core::fmt::Debug>( + p: Vec3, + dir: Vec3, + bounds: Aabb, +) -> Option> { + 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 + } + }) +} + +pub fn include_object_light_volume< + T: Float + MulAdd + core::fmt::Debug, + I: Iterator>, +>( + obj: I, + light_dir: Vec3, + bounds: Aabb, +) -> impl Iterator> { + obj.flat_map(move |pt| iter::once(pt).chain(intersection_line_aabb(pt, -light_dir, bounds))) +} + +// NOTE: Currently specialized to skip extending to the end of the light ray, +// since our light ray is already infinite. Correct code is commented out +// below. +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); + let mut world_frust_object = calc_view_frust_object(&world_pts); + clip_object_by_aabb(&mut world_frust_object, scene_bounding_box, tolerance); + 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, + ) */ +} + +/// NOTE: Will not yield useful results if pts is empty! +pub fn fit_psr< + T: Float + MulAdd, + I: Iterator>, + F: FnMut(Vec4) -> Vec4, +>( + mat: Mat4, + pts: I, + mut do_p: F, +) -> Aabb { + let mut min = Vec4::broadcast(T::infinity()); + let mut max = Vec4::broadcast(T::neg_infinity()); + pts.map(|p| do_p(mat * Vec4::::from_point(p))) + .for_each(|p| { + min = Vec4::partial_min(min, p); + max = Vec4::partial_max(max, p); + }); + Aabb { + min: min.xyz(), + max: max.xyz(), + } +} diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index c4e338d463..d69cf98c2c 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1,5 +1,7 @@ pub mod camera; pub mod figure; +pub mod lod; +pub mod math; pub mod particle; pub mod simple; pub mod terrain; @@ -7,18 +9,22 @@ pub mod terrain; pub use self::{ camera::{Camera, CameraMode}, figure::FigureMgr, + lod::Lod, particle::ParticleMgr, terrain::Terrain, }; use crate::{ audio::{music::MusicMgr, sfx::SfxMgr, AudioFrontend}, render::{ - create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals, - PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, + create_pp_mesh, create_skybox_mesh, Consts, GlobalModel, Globals, Light, LodData, Model, + PostProcessLocals, PostProcessPipeline, Renderer, Shadow, ShadowLocals, SkyboxLocals, + SkyboxPipeline, }, + settings::Settings, window::{AnalogGameInput, Event}, }; use anim::character::SkeletonAttr; +use client::Client; use common::{ comp, outcome::Outcome, @@ -27,22 +33,32 @@ use common::{ vol::ReadVol, }; use comp::item::Reagent; +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 = 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_UPSILON: f64 = -1.0; + +const SHADOW_NEAR: f32 = 0.25; // Near plane for shadow map point light rendering. +const SHADOW_FAR: f32 = 128.0; // Far plane for shadow map point light rendering. /// Above this speed is considered running /// Used for first person camera effects const RUNNING_THRESHOLD: f32 = 0.7; +/// is_daylight, array of active lights. +pub type LightData<'a> = (bool, &'a [Light]); + struct EventLight { light: Light, timeout: f32, @@ -60,9 +76,7 @@ struct PostProcess { } pub struct Scene { - globals: Consts, - lights: Consts, - shadows: Consts, + data: GlobalModel, camera: Camera, camera_input_state: Vec2, event_lights: Vec, @@ -70,8 +84,14 @@ pub struct Scene { skybox: Skybox, postprocess: PostProcess, terrain: Terrain, + pub lod: Lod, loaded_distance: f32, + /// x coordinate is sea level (minimum height for any land chunk), and y + /// coordinate is the maximum height above the mnimimum for any land + /// chunk. + map_bounds: Vec2, select_pos: Option>, + light_data: Vec, particle_mgr: ParticleMgr, figure_mgr: FigureMgr, @@ -95,19 +115,164 @@ 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()) } +} + +/// 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(); + theta_x.min(theta_y) +} + +/// 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 ≤ γ +/// +/// NOTE: Equation's described behavior is *wrong!* I have pieced together a +/// slightly different function that seems to more closely satisfy the author's +/// intent: +/// +/// η = +/// -1 γ < γ_a +/// -1 + (η_b + 1) (γ - γ_a)/(γ_b - γ_a) γ_a ≤ γ < γ_b +/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c +/// η_c γ_c ≤ γ +/// +/// There are other alternatives that may have more desirable properties, such +/// as: +/// +/// η = +/// -1 γ < γ_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::one() + /* 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()) */ + -F::one() + (eta_b + F::one()) * (F::one() - (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos()) + // -F::one() + (eta_b + F::one()) * ((gamma - gamma_a) / (gamma_b - gamma_a)) + } 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()), + ) +} + impl Scene { /// Create a new `Scene` with default parameters. - pub fn new(renderer: &mut Renderer) -> Self { + pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self { let resolution = renderer.get_resolution().map(|e| e as f32); Self { - globals: renderer.create_consts(&[Globals::default()]).unwrap(), - lights: renderer - .create_consts(&[Light::default(); MAX_LIGHT_COUNT]) - .unwrap(), - shadows: renderer - .create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]) - .unwrap(), + data: GlobalModel { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + lights: renderer + .create_consts(&[Light::default(); MAX_LIGHT_COUNT]) + .unwrap(), + shadows: renderer + .create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]) + .unwrap(), + shadow_mats: renderer + .create_consts(&[ShadowLocals::default(); MAX_LIGHT_COUNT * 6 + 6]) + .unwrap(), + }, camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson), camera_input_state: Vec2::zero(), event_lights: Vec::new(), @@ -123,18 +288,20 @@ impl Scene { .unwrap(), }, terrain: Terrain::new(renderer), + lod: Lod::new(renderer, client, settings), loaded_distance: 0.0, + map_bounds: client.world_map.2, select_pos: None, - + light_data: Vec::new(), particle_mgr: ParticleMgr::new(renderer), - figure_mgr: FigureMgr::new(), + figure_mgr: FigureMgr::new(renderer), sfx_mgr: SfxMgr::new(), music_mgr: MusicMgr::new(), } } /// Get a reference to the scene's globals. - pub fn globals(&self) -> &Consts { &self.globals } + pub fn globals(&self) -> &Consts { &self.data.globals } /// Get a reference to the scene's camera. pub fn camera(&self) -> &Camera { &self.camera } @@ -142,6 +309,9 @@ impl Scene { /// Get a reference to the scene's terrain. pub fn terrain(&self) -> &Terrain { &self.terrain } + /// Get a reference to the scene's lights. + pub fn lights(&self) -> &Vec { &self.light_data } + /// Get a reference to the scene's particle manager. pub fn particle_mgr(&self) -> &ParticleMgr { &self.particle_mgr } @@ -328,55 +498,60 @@ 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, - ) - }) - .chain( - self.event_lights - .iter() - .map(|el| el.light.with_strength((el.fadeout)(el.timeout))), + 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::(), ) - .collect::>(); + .join() + .filter(|(pos, _, _, light_anim)| { + light_anim.col != Rgb::zero() + && light_anim.strength > 0.0 + && (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, + ) + }) + .chain( + self.event_lights + .iter() + .map(|el| el.light.with_strength((el.fadeout)(el.timeout))), + ), + ); lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32); lights.truncate(MAX_LIGHT_COUNT); renderer - .update_consts(&mut self.lights, &lights) + .update_consts(&mut self.data.lights, &lights) .expect("Failed to update light constants"); // Update event lights @@ -402,7 +577,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( @@ -415,47 +590,353 @@ impl Scene { shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32); shadows.truncate(MAX_SHADOW_COUNT); renderer - .update_consts(&mut self.shadows, &shadows) + .update_consts(&mut self.data.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 focus_pos = self.camera.get_focus_pos(); + let focus_off = focus_pos.map(|e| e.trunc()); + // Update global constants. renderer - .update_consts(&mut self.globals, &[Globals::new( + .update_consts(&mut self.data.globals, &[Globals::new( view_mat, proj_mat, cam_pos, - self.camera.get_focus_pos(), + focus_pos, self.loaded_distance, - scene_data.state.get_time_of_day(), + self.lod.get_data().tgt_detail as f32, + self.map_bounds, + time_of_day, scene_data.state.get_time(), renderer.get_resolution(), + Vec2::new(SHADOW_NEAR, SHADOW_FAR), lights.len(), shadows.len(), + NUM_DIRECTED_LIGHTS, 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, )]) .expect("Failed to update global constants"); + // Maintain LoD. + self.lod.maintain(renderer); + // Maintain the terrain. - self.terrain.maintain( + let (_visible_bounds, visible_light_volume, visible_psr_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, visible_psr_bounds, &self.camera); + + let sun_dir = scene_data.get_sun_dir(); + let is_daylight = sun_dir.z < 0.0; + if renderer.render_mode().shadow.is_map() && (is_daylight || !lights.is_empty()) { + let fov = self.camera.get_fov(); + let aspect_ratio = self.camera.get_aspect_ratio(); + + 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 = math::Vec3::from(sun_dir); + + // 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. + // 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 look_at = math::Vec3::from(cam_pos); + // 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 = math::Vec3::from(view_dir); + let new_dir = new_dir.normalized(); + let up: math::Vec3 = math::Vec3::up(); + directed_shadow_mats.push(math::Mat4::look_at_rh( + look_at, + look_at + directed_light_dir, + up, + )); + // This leaves us with five dummy slots, which we push as defaults. + directed_shadow_mats + .extend_from_slice(&[math::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)); + shadow_mats.extend(directed_shadow_mats.iter().enumerate().map( + move |(idx, &light_view_mat)| { + if idx >= NUM_DIRECTED_LIGHTS { + return ShadowLocals::new(Mat4::identity(), Mat4::identity()); + } + + let v_p_orig = + math::Vec3::from(light_view_mat * math::Vec4::from_direction(new_dir)); + let mut v_p = v_p_orig.normalized(); + let cos_gamma = new_dir + .map(f64::from) + .dot(directed_light_dir.map(f64::from)); + let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt(); + let gamma = sin_gamma.asin(); + let view_mat = math::Mat4::from_col_array(view_mat.into_col_array()); + let bounds1 = math::fit_psr( + view_mat.map_cols(math::Vec4::from), + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + let n_e = f64::from(-bounds1.max.z); + let factor = compute_warping_parameter_perspective( + gamma, + n_e, + f64::from(fov), + f64::from(aspect_ratio), + ); + + v_p.z = 0.0; + v_p.normalize(); + let l_r: math::Mat4 = if factor > EPSILON_UPSILON { + math::Mat4::look_at_rh(math::Vec3::zero(), math::Vec3::forward_rh(), v_p) + } else { + math::Mat4::identity() + }; + let directed_proj_mat = math::Mat4::new( + 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, + 1.0, + ); + + let light_all_mat = l_r * directed_proj_mat * light_view_mat; + let bounds0 = math::fit_psr( + light_all_mat, + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + // Vague idea: project z_n from the camera view to the light view (where it's + // tilted by γ). + let (z_0, z_1) = { + let p_z = bounds1.max.z; + let p_y = bounds0.min.y; + let p_x = bounds0.center().x; + let view_inv = view_mat.inverted(); + let light_all_inv = light_all_mat.inverted(); + + let view_point = view_inv * math::Vec4::new(0.0, 0.0, p_z, 1.0); + let view_plane = + view_inv * math::Vec4::from_direction(math::Vec3::unit_z()); + + let light_point = light_all_inv * math::Vec4::new(0.0, p_y, 0.0, 1.0); + let light_plane = + light_all_inv * math::Vec4::from_direction(math::Vec3::unit_y()); + + let shadow_point = light_all_inv * math::Vec4::new(p_x, 0.0, 0.0, 1.0); + let shadow_plane = + light_all_inv * math::Vec4::from_direction(math::Vec3::unit_x()); + + let solve_p0 = math::Mat4::new( + view_plane.x, + view_plane.y, + view_plane.z, + -view_plane.dot(view_point), + light_plane.x, + light_plane.y, + light_plane.z, + -light_plane.dot(light_point), + shadow_plane.x, + shadow_plane.y, + shadow_plane.z, + -shadow_plane.dot(shadow_point), + 0.0, + 0.0, + 0.0, + 1.0, + ); + + let p0_world = solve_p0.inverted() * math::Vec4::unit_w(); + let p0 = light_all_mat * p0_world; + let mut p1 = p0; + p1.y = bounds0.max.y; + + let view_from_light_mat = view_mat * light_all_inv; + let z0 = view_from_light_mat * p0; + let z1 = view_from_light_mat * p1; + + (f64::from(z0.z), f64::from(z1.z)) + }; + + let mut light_focus_pos: math::Vec3 = math::Vec3::zero(); + light_focus_pos.x = bounds0.center().x; + light_focus_pos.y = bounds0.min.y; + light_focus_pos.z = bounds0.center().z; + + let d = f64::from(bounds0.max.y - bounds0.min.y).abs(); + + let w_l_y = d; + + // NOTE: See section 5.1.2.2 of Lloyd's thesis. + let alpha = z_1 / z_0; + 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)) + } else { + // LiSPSM to PSM + ((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip() + }; + + // Equation 5.14 - 5.16 + let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs(); + let directed_near = y_(0.0) as f32; + let directed_far = y_(1.0) as f32; + light_focus_pos.y = if factor > EPSILON_UPSILON { + light_focus_pos.y - directed_near + } else { + light_focus_pos.y + }; + let w_v: math::Mat4 = math::Mat4::translation_3d(-math::Vec3::new( + light_focus_pos.x, + light_focus_pos.y, + light_focus_pos.z, + )); + let shadow_view_mat: math::Mat4 = w_v * light_all_mat; + let w_p: math::Mat4 = { + if factor > EPSILON_UPSILON { + // Projection for y + let near = directed_near; + let far = directed_far; + let left = -1.0; + let right = 1.0; + let bottom = -1.0; + let top = 1.0; + let s_x = 2.0 * near / (right - left); + let o_x = (right + left) / (right - left); + let s_z = 2.0 * near / (top - bottom); + let o_z = (top + bottom) / (top - bottom); + + let s_y = (far + near) / (far - near); + let o_y = -2.0 * far * near / (far - near); + + math::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, + ) + } else { + math::Mat4::identity() + } + }; + + let shadow_all_mat: math::Mat4 = w_p * shadow_view_mat; + let math::Aabb:: { + min: + math::Vec3 { + x: xmin, + y: ymin, + z: zmin, + }, + max: + math::Vec3 { + x: xmax, + y: ymax, + z: zmax, + }, + } = math::fit_psr( + shadow_all_mat, + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + let s_x = 2.0 / (xmax - xmin); + let s_y = 2.0 / (ymax - ymin); + let s_z = 2.0 / (zmax - zmin); + 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 = 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, + ); + + let shadow_all_mat: Mat4 = + Mat4::from_col_arrays(shadow_all_mat.into_col_arrays()); + + 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(), + ) + }) + })); + + renderer + .update_consts(&mut self.data.shadow_mats, &shadow_mats) + .expect("Failed to update light constants"); + } // Remove unused figures. self.figure_mgr.clean(scene_data.tick); @@ -482,61 +963,79 @@ impl Scene { tick: u64, scene_data: &SceneData, ) { - // Render terrain and figures. - self.terrain.render( - renderer, - &self.globals, - &self.lights, - &self.shadows, - self.camera.get_focus_pos(), - ); - self.figure_mgr.render( - renderer, - state, - player_entity, - tick, - &self.globals, - &self.lights, - &self.shadows, - &self.camera, - scene_data.figure_lod_render_distance, - ); + let sun_dir = scene_data.get_sun_dir(); + let is_daylight = sun_dir.z < 0.0; + let focus_pos = self.camera.get_focus_pos(); + let cam_pos = self.camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); - self.particle_mgr.render( - renderer, - scene_data, - &self.globals, - &self.lights, - &self.shadows, - ); + let global = &self.data; + let light_data = (is_daylight, &*self.light_data); + let camera_data = (&self.camera, scene_data.figure_lod_render_distance); - // Render the skybox. - renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); + // would instead have this as an extension. + if renderer.render_mode().shadow.is_map() && (is_daylight || !light_data.1.is_empty()) { + if is_daylight { + // Set up shadow mapping. + renderer.start_shadows(); + } + + // Render terrain shadows. + self.terrain + .render_shadows(renderer, global, light_data, focus_pos); + + // Render figure shadows. + self.figure_mgr + .render_shadows(renderer, state, tick, global, light_data, camera_data); + + if is_daylight { + // Flush shadows. + renderer.flush_shadows(); + } + } + let lod = self.lod.get_data(); self.figure_mgr.render_player( renderer, state, player_entity, tick, - &self.globals, - &self.lights, - &self.shadows, - &self.camera, - scene_data.figure_lod_render_distance, + global, + lod, + camera_data, ); + // Render terrain and figures. + self.terrain.render(renderer, global, lod, focus_pos); + + self.figure_mgr.render( + renderer, + state, + player_entity, + tick, + global, + lod, + camera_data, + ); + self.lod.render(renderer, global); + + // Render particle effects. + self.particle_mgr.render(renderer, scene_data, global, lod); + + // Render the skybox. + renderer.render_skybox(&self.skybox.model, global, &self.skybox.locals, lod); + self.terrain.render_translucent( renderer, - &self.globals, - &self.lights, - &self.shadows, - self.camera.get_focus_pos(), + global, + lod, + focus_pos, + cam_pos, scene_data.sprite_render_distance, ); renderer.render_post_process( &self.postprocess.model, - &self.globals, + &global.globals, &self.postprocess.locals, ); } diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 7bc5fb6ff3..32f6ce5c49 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -1,9 +1,9 @@ use super::SceneData; use crate::{ - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - pipelines::particle::ParticleMode, Consts, Globals, Instances, Light, Model, - ParticleInstance, ParticlePipeline, Renderer, Shadow, + pipelines::particle::ParticleMode, GlobalModel, Instances, LodData, Model, + ParticleInstance, ParticlePipeline, Renderer, }, }; use common::{ @@ -43,7 +43,6 @@ impl ParticleMgr { } } - #[allow(clippy::same_item_push)] // TODO: Pending review in #587 pub fn handle_outcome(&mut self, outcome: &Outcome, scene_data: &SceneData) { let time = scene_data.state.get_time(); let mut rng = rand::thread_rng(); @@ -54,8 +53,9 @@ impl ParticleMgr { power, reagent, } => { - for _ in 0..150 { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + 150, + Particle::new( Duration::from_millis(if reagent.is_some() { 1000 } else { 250 }), time, match reagent { @@ -67,17 +67,17 @@ impl ParticleMgr { None => ParticleMode::Shrapnel, }, *pos, - )); - } + ), + ); - for _ in 0..200 { - self.particles.push(Particle::new( + self.particles.resize_with(self.particles.len() + 200, || { + Particle::new( Duration::from_secs(4), time, ParticleMode::CampfireSmoke, *pos + Vec2::::zero().map(|_| rng.gen_range(-1.0, 1.0) * power), - )); - } + ) + }); }, Outcome::ProjectileShot { .. } => {}, } @@ -108,14 +108,7 @@ impl ParticleMgr { fn maintain_body_particles(&mut self, scene_data: &SceneData) { let ecs = scene_data.state.ecs(); - for (_i, (_entity, body, pos)) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ) - .join() - .enumerate() - { + for (body, pos) in (&ecs.read_storage::(), &ecs.read_storage::()).join() { match body { Body::Object(object::Body::CampfireLit) => { self.maintain_campfirelit_particles(scene_data, pos) @@ -178,29 +171,30 @@ impl ParticleMgr { } } - #[allow(clippy::same_item_push)] // TODO: Pending review in #587 fn maintain_boltfirebig_particles(&mut self, scene_data: &SceneData, pos: &Pos) { let time = scene_data.state.get_time(); // fire - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(3)) { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(3))), + Particle::new( Duration::from_millis(250), time, ParticleMode::CampfireFire, pos.0, - )); - } + ), + ); // smoke - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(5)) { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))), + Particle::new( Duration::from_secs(2), time, ParticleMode::CampfireSmoke, pos.0, - )); - } + ), + ); } fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos) { @@ -225,29 +219,28 @@ impl ParticleMgr { } } - #[allow(clippy::same_item_push)] // TODO: Pending review in #587 fn maintain_boost_particles(&mut self, scene_data: &SceneData) { let state = scene_data.state; let ecs = state.ecs(); let time = state.get_time(); - for (_i, (_entity, pos, character_state)) in ( - &ecs.entities(), + for (pos, character_state) in ( &ecs.read_storage::(), &ecs.read_storage::(), ) .join() - .enumerate() { if let CharacterState::Boost(_) = character_state { - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(10)) { - self.particles.push(Particle::new( + self.particles.resize( + self.particles.len() + + usize::from(self.scheduler.heartbeats(Duration::from_millis(10))), + Particle::new( Duration::from_secs(15), time, ParticleMode::CampfireSmoke, pos.0, - )); - } + ), + ); } } } @@ -271,9 +264,8 @@ impl ParticleMgr { &self, renderer: &mut Renderer, scene_data: &SceneData, - globals: &Consts, - lights: &Consts, - shadows: &Consts, + global: &GlobalModel, + lod: &LodData, ) { if scene_data.particles_enabled { let model = &self @@ -281,7 +273,7 @@ impl ParticleMgr { .get(DEFAULT_MODEL_KEY) .expect("Expected particle model in cache"); - renderer.render_particles(model, globals, &self.instances, lights, shadows); + renderer.render_particles(model, global, &self.instances, lod); } } @@ -304,19 +296,26 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model(DEFAULT_MODEL_KEY); - let mesh = &Meshable::::generate_mesh( - &Segment::from(vox.as_ref()), - (offset * lod_scale, Vec3::one() / lod_scale), + // NOTE: If we add texturing we may eventually try to share it among all + // particles in a single atlas. + 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 mut greedy = GreedyMesh::new(max_size); + + let mesh = Meshable::::generate_mesh( + Segment::from(vox.as_ref()), + &mut greedy, ) .0; + // NOTE: Ignoring coloring / lighting for now. + drop(greedy); + renderer - .create_model(mesh) + .create_model(&mesh) .expect("Failed to create particle model") }); @@ -390,6 +389,7 @@ impl HeartbeatScheduler { pub fn clear(&mut self) { self.timers.clear() } } +#[derive(Clone, Copy)] struct Particle { alive_until: f64, // created_at + lifespan instance: ParticleInstance, diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 259419fee6..785b8a6784 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -1,20 +1,23 @@ use crate::{ - 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, + GlobalModel, Globals, Light, Mesh, Model, PostProcessLocals, PostProcessPipeline, Renderer, + Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline, TerrainPipeline, }, scene::{ camera::{self, Camera, CameraMode}, - figure::{load_mesh, FigureModelCache, FigureState}, + figure::{load_mesh, FigureColLights, FigureModelCache, FigureModelEntry, FigureState}, + LodData, }, window::{Event, PressState}, }; use anim::{ character::{CharacterSkeleton, IdleAnimation, SkeletonAttr}, fixture::FixtureSkeleton, - Animation, Skeleton, + Animation, }; +use client::Client; use common::{ comp::{humanoid, item::ItemKind, Body, Loadout}, figure::Segment, @@ -42,8 +45,18 @@ 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>, + mesh: &mut Mesh, + segment: Segment, + offset: Vec3, +) -> BoneMeshes { + let (opaque, _, /* shadow */ _, bounds) = + Meshable::::generate_mesh( + segment, + (greedy, mesh, offset, Vec3::one()), + ); + (opaque /* , shadow */, bounds) } struct Skybox { @@ -57,15 +70,16 @@ struct PostProcess { } pub struct Scene { - globals: Consts, - lights: Consts, - shadows: Consts, + data: GlobalModel, camera: Camera, skybox: Skybox, postprocess: PostProcess, - backdrop: Option<(Model, FigureState)>, + lod: LodData, + map_bounds: Vec2, + col_lights: FigureColLights, + backdrop: Option<(FigureModelEntry<1>, FigureState)>, figure_model_cache: FigureModelCache, figure_state: FigureState, @@ -84,19 +98,32 @@ pub struct SceneData { } impl Scene { - pub fn new(renderer: &mut Renderer, backdrop: Option<&str>) -> Self { + pub fn new(renderer: &mut Renderer, backdrop: Option<&str>, client: &Client) -> Self { + let start_angle = 90.0f32.to_radians(); let resolution = renderer.get_resolution().map(|e| e as f32); + let map_bounds = client.world_map.2; + let map_border = [0.0, 0.0, 0.0, 0.0]; + let map_image = [0]; + let alt_image = [0]; + let horizon_image = [0x_00_01_00_01]; + let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson); camera.set_focus_pos(Vec3::unit_z() * 1.5); camera.set_distance(3.4); - camera.set_orientation(Vec3::new(0.0, 0.0, 0.0)); + 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, + data: GlobalModel { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), + shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), + shadow_mats: renderer + .create_consts(&[ShadowLocals::default(); 6]) + .unwrap(), + }, skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), @@ -108,28 +135,65 @@ impl Scene { .create_consts(&[PostProcessLocals::default()]) .unwrap(), }, + lod: LodData::new( + renderer, + Vec2::new(1, 1), + &map_image, + &alt_image, + &horizon_image, + 1, + map_border.into(), + ), + map_bounds, + figure_model_cache: FigureModelCache::new(), - figure_state: FigureState::new(renderer, CharacterSkeleton::new()), + figure_state: FigureState::new(renderer, CharacterSkeleton::default()), backdrop: backdrop.map(|specifier| { - ( - renderer - .create_model(&load_mesh( - specifier, - Vec3::new(-55.0, -49.5, -2.0), - generate_mesh, - )) - .unwrap(), - FigureState::new(renderer, FixtureSkeleton::new()), - ) + let mut state = FigureState::new(renderer, FixtureSkeleton::default()); + let mut greedy = FigureModel::make_greedy(); + let mut opaque_mesh = Mesh::new(); + let (_opaque_mesh, (bounds, range)) = load_mesh( + specifier, + Vec3::new(-55.0, -49.5, -2.0), + |segment, offset| generate_mesh(&mut greedy, &mut opaque_mesh, segment, offset), + ); + // NOTE: Since MagicaVoxel sizes are limited to 256 × 256 × 256, and there are + // at most 3 meshed vertices per unique vertex, we know the + // total size is bounded by 2^24 * 3 * 1.5 which is bounded by + // 2^27, which fits in a u32. + let range = range.start as u32..range.end as u32; + let model = col_lights + .create_figure(renderer, greedy, (opaque_mesh, bounds), [range]) + .unwrap(); + let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; + state.update( + renderer, + anim::vek::Vec3::zero(), + anim::vek::Vec3::new(start_angle.sin(), -start_angle.cos(), 0.0), + 1.0, + Rgba::broadcast(1.0), + 15.0, // Want to get there immediately. + 1.0, + &model, + 0, + true, + false, + &camera, + &mut buf, + ); + (model, state) }), + col_lights, + + camera, turning: false, - char_ori: 0.0, + char_ori: -start_angle, } } - pub fn globals(&self) -> &Consts { &self.globals } + pub fn globals(&self) -> &Consts { &self.data.globals } pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera } @@ -163,8 +227,11 @@ impl Scene { scene_data: SceneData, loadout: Option<&Loadout>, ) { - self.camera - .update(scene_data.time, 1.0 / 60.0, scene_data.mouse_smoothing); + self.camera.update( + scene_data.time, + /* 1.0 / 60.0 */ scene_data.delta_time, + scene_data.mouse_smoothing, + ); self.camera.compute_dependents(&VoidVol); let camera::Dependents { @@ -174,16 +241,23 @@ impl Scene { .. } = self.camera.dependents(); const VD: f32 = 115.0; // View Distance - const TIME: f64 = 43200.0; // 12 hours*3600 seconds - if let Err(e) = renderer.update_consts(&mut self.globals, &[Globals::new( + const TIME: f64 = 10.0 * 60.0 * 60.0; + const SHADOW_NEAR: f32 = 1.0; + const SHADOW_FAR: f32 = 25.0; + + if let Err(e) = renderer.update_consts(&mut self.data.globals, &[Globals::new( view_mat, proj_mat, cam_pos, self.camera.get_focus_pos(), VD, + self.lod.tgt_detail as f32, + self.map_bounds, TIME, scene_data.time, renderer.get_resolution(), + Vec2::new(SHADOW_NEAR, SHADOW_FAR), + 0, 0, 0, BlockKind::Air, @@ -195,7 +269,8 @@ impl Scene { error!(?e, "Renderer failed to update"); } - self.figure_model_cache.clean(scene_data.tick); + self.figure_model_cache + .clean(&mut self.col_lights, scene_data.tick); let active_item_kind = loadout .and_then(|l| l.active_item.as_ref()) @@ -225,23 +300,39 @@ impl Scene { &mut 0.0, &SkeletonAttr::from(&body), ); - self.figure_state - .skeleton_mut() - .interpolate(&tgt_skeleton, scene_data.delta_time); - } + let dt_lerp = (scene_data.delta_time * 15.0).min(1.0); + *self.figure_state.skeleton_mut() = + anim::vek::Lerp::lerp(&*self.figure_state.skeleton_mut(), &tgt_skeleton, dt_lerp); - 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), - 1.0 / 60.0, // TODO: Use actual deltatime here? - 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; + let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; + self.figure_state.update( + renderer, + anim::vek::Vec3::zero(), + anim::vek::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, + true, + false, + &self.camera, + &mut buf, + ); + } } pub fn render( @@ -251,13 +342,19 @@ impl Scene { body: Option, loadout: Option<&Loadout>, ) { - renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); + renderer.render_skybox( + &self.skybox.model, + &self.data, + &self.skybox.locals, + &self.lod, + ); if let Some(body) = body { let model = &self .figure_model_cache .get_or_create_model( renderer, + &mut self.col_lights, Body::Humanoid(body), loadout, tick, @@ -267,29 +364,29 @@ impl Scene { .0; renderer.render_figure( - &model[0], - &self.globals, + &model.models[0], + &self.col_lights.texture(model), + &self.data, self.figure_state.locals(), self.figure_state.bone_consts(), - &self.lights, - &self.shadows, + &self.lod, ); } if let Some((model, state)) = &self.backdrop { renderer.render_figure( - model, - &self.globals, + &model.models[0], + &self.col_lights.texture(model), + &self.data, state.locals(), state.bone_consts(), - &self.lights, - &self.shadows, + &self.lod, ); } renderer.render_post_process( &self.postprocess.model, - &self.globals, + &self.data.globals, &self.postprocess.locals, ); } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index f3d95593ad..06ec276b93 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1,12 +1,13 @@ use crate::{ - mesh::Meshable, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, Shadow, - SpriteInstance, SpritePipeline, TerrainLocals, TerrainPipeline, Texture, + ColLightFmt, ColLightInfo, Consts, FluidPipeline, GlobalModel, Instances, Mesh, Model, + RenderError, Renderer, ShadowPipeline, SpriteInstance, SpriteLocals, SpritePipeline, + TerrainLocals, TerrainPipeline, Texture, }, }; -use super::SceneData; +use super::{math, LodData, SceneData}; use common::{ assets, figure::Segment, @@ -15,23 +16,37 @@ 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 tracing::warn; 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>, + col_lights: guillotiere::AllocId, sprite_instances: HashMap<(BlockKind, usize), Instances>, locals: Consts, - visible: bool, + visible: Visibility, + can_shadow_point: bool, + can_shadow_sun: bool, z_bounds: (f32, f32), frustum_last_plane_index: u8, } @@ -49,6 +64,7 @@ struct MeshWorkerResponse { z_bounds: (f32, f32), opaque_mesh: Mesh, fluid_mesh: Mesh, + col_lights_info: ColLightInfo, sprite_instances: HashMap<(BlockKind, usize), Vec>, started_tick: u64, } @@ -143,7 +159,7 @@ fn sprite_config_for(kind: BlockKind) -> Option { wind_sway: 0.1, }), BlockKind::LargeGrass => Some(SpriteConfig { - variations: 1, + variations: 3, wind_sway: 0.5, }), @@ -375,14 +391,18 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( z_bounds: (f32, f32), started_tick: u64, volume: as SampleVol>>::Sample, + max_texture_size: u16, range: Aabb, + sprite_data: &HashMap<(BlockKind, usize), Vec>, ) -> MeshWorkerResponse { - let (opaque_mesh, fluid_mesh) = volume.generate_mesh(range); + let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info)) = + volume.generate_mesh((range, Vec2::new(max_texture_size, max_texture_size))); MeshWorkerResponse { pos, - z_bounds, + z_bounds: (bounds.min.z, bounds.max.z), opaque_mesh, fluid_mesh, + col_lights_info, // Extract sprite locations from volume sprite_instances: { let mut instances = HashMap::new(); @@ -390,8 +410,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()); @@ -399,22 +419,26 @@ 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_data[&key][0]; let instance = SpriteInstance::new( Mat4::identity() + .translated_3d(sprite_data.offset) .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), cfg.wind_sway, + rel_pos, + ori, ); - instances - .entry((block.kind(), seed as usize % cfg.variations)) - .or_insert_with(Vec::new) - .push(instance); + instances.entry(key).or_insert(Vec::new()).push(instance); } } } @@ -426,9 +450,32 @@ 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>, + /// Temporary storage for dead chunks that might still be shadowing chunks + /// in view. We wait until either the chunk definitely cannot be + /// shadowing anything the player can see, the chunk comes back into + /// view, or for daylight to end, before removing it (whichever comes + /// first). + /// + /// Note that these chunks are not complete; for example, they are missing + /// texture data. + shadow_chunks: Vec<(Vec2, 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. @@ -437,12 +484,20 @@ pub struct Terrain { mesh_todo: HashMap, ChunkMeshState>, // GPU data - sprite_models: HashMap<(BlockKind, usize), Vec>>, + sprite_data: Arc>>, + 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 { #[allow(clippy::float_cmp)] // TODO: Pending review in #587 pub fn new(renderer: &mut Renderer) -> Self { @@ -450,2414 +505,1962 @@ impl Terrain { // 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 mut greedy = GreedyMesh::new(max_size); + let mut locals_buffer = [SpriteLocals::default(); 8]; + // NOTE: Tracks the start vertex of the next model to be meshed. + 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 zero = Vec3::zero(); + let model_size = model + .models + .first() + .map( + |&dot_vox::Model { + size: dot_vox::Size { x, y, z }, + .. + }| Vec3::new(x, y, z), + ) + .unwrap_or(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 + } + }); + 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 }) + }; + // Mesh generation exclusively acts using side effects; it has no + // interesting return value, but updates the mesh. + let mut opaque_mesh = Mesh::new(); + Meshable::::generate_mesh( + Segment::from(model.as_ref()).scaled_by(lod_scale), + ( + &mut greedy, + &mut opaque_mesh, + wind_sway >= 0.4 && lod_scale_orig == 1.0, + ), + ); + let model = renderer + .create_model(&opaque_mesh) + .expect("Failed to upload sprite model data to the GPU!"); + + 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 { + /* vertex_range */ model, + offset, + locals: renderer + .create_consts(&locals_buffer) + .expect("Failed to upload sprite locals to the GPU!"), + } + }) + .collect::>(), + ) }; + let sprite_data: HashMap<(BlockKind, usize), _> = 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::BlueFlower, 7), + "voxygen.voxel.sprite.flowers.flower_blue-8", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 8), + "voxygen.voxel.sprite.flowers.flower_blue-9", + Vec3::new(-4.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::BlueFlower, 9), + "voxygen.voxel.sprite.flowers.flower_blue-10", + Vec3::new(-1.5, -1.5, 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::PurpleFlower, 1), + "voxygen.voxel.sprite.flowers.flower_purple-2", + Vec3::new(-5.0, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 2), + "voxygen.voxel.sprite.flowers.flower_purple-3", + Vec3::new(-3.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 3), + "voxygen.voxel.sprite.flowers.flower_purple-4", + Vec3::new(-5.0, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 4), + "voxygen.voxel.sprite.flowers.flower_purple-5", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 5), + "voxygen.voxel.sprite.flowers.flower_purple-6", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 6), + "voxygen.voxel.sprite.flowers.flower_purple-7", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::PurpleFlower, 7), + "voxygen.voxel.sprite.flowers.flower_purple-8", + 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::RedFlower, 3), + "voxygen.voxel.sprite.flowers.flower_red-4", + Vec3::new(-6.5, -6.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::RedFlower, 4), + "voxygen.voxel.sprite.flowers.flower_red-5", + Vec3::new(-3.5, -3.5, 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::WhiteFlower, 2), + "voxygen.voxel.sprite.flowers.flower_white-3", + Vec3::new(-1.5, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 3), + "voxygen.voxel.sprite.flowers.flower_white-4", + Vec3::new(-5.0, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WhiteFlower, 4), + "voxygen.voxel.sprite.flowers.flower_white-5", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::YellowFlower, 0), + "voxygen.voxel.sprite.flowers.flower_yellow-1", + Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::YellowFlower, 1), + "voxygen.voxel.sprite.flowers.flower_yellow-0", + Vec3::new(-5.5, -5.5, 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::LargeGrass, 0), + "voxygen.voxel.sprite.grass.grass_large-0", + Vec3::new(-2.0, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LargeGrass, 1), + "voxygen.voxel.sprite.grass.grass_large-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::LargeGrass, 2), + "voxygen.voxel.sprite.grass.grass_large-2", + Vec3::new(-5.5, -5.0, 0.0), + Vec3::one(), + ), + 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::Mushroom, 11), + "voxygen.voxel.sprite.mushrooms.mushroom-11", + Vec3::new(-8.0, -8.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 12), + "voxygen.voxel.sprite.mushrooms.mushroom-12", + Vec3::new(-5.0, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 13), + "voxygen.voxel.sprite.mushrooms.mushroom-13", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 14), + "voxygen.voxel.sprite.mushrooms.mushroom-14", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 15), + "voxygen.voxel.sprite.mushrooms.mushroom-15", + Vec3::new(-1.5, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Mushroom, 16), + "voxygen.voxel.sprite.mushrooms.mushroom-16", + Vec3::new(-5.5, -5.5, 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(), + ), + make_models( + (BlockKind::Fern, 12), + "voxygen.voxel.sprite.ferns.fern-0", + Vec3::new(-6.5, -11.5, 0.0), + Vec3::unit_z(), + ), + // 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(), + ), + make_models( + (BlockKind::StreetLampTall, 0), + "voxygen.voxel.sprite.furniture.street_lamp-0", + Vec3::new(-10.5, -10.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(), + ), + // Bed + make_models( + (BlockKind::Bed, 0), + "voxygen.voxel.sprite.furniture.bed-0", + Vec3::new(-9.5, -14.5, 0.0), + Vec3::one(), + ), + // Bench + make_models( + (BlockKind::Bench, 0), + "voxygen.voxel.sprite.furniture.bench-0", + Vec3::new(-14.0, -4.0, 0.0), + Vec3::one(), + ), + // Chair + make_models( + (BlockKind::ChairSingle, 0), + "voxygen.voxel.sprite.furniture.chair_single-0", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ChairSingle, 1), + "voxygen.voxel.sprite.furniture.chair_single-1", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ChairDouble, 0), + "voxygen.voxel.sprite.furniture.chair_double-0", + Vec3::new(-9.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ChairDouble, 1), + "voxygen.voxel.sprite.furniture.chair_double-1", + Vec3::new(-9.5, -4.5, 0.0), + Vec3::one(), + ), + // CoatRack + make_models( + (BlockKind::CoatRack, 0), + "voxygen.voxel.sprite.furniture.coatrack-0", + Vec3::new(-6.5, -6.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::CoatRack, 1), + "voxygen.voxel.sprite.furniture.coatrack-1", + Vec3::new(-6.5, -6.5, 0.0), + Vec3::one(), + ), + // Crate + make_models( + (BlockKind::Crate, 0), + "voxygen.voxel.sprite.furniture.crate-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 1), + "voxygen.voxel.sprite.furniture.crate-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 2), + "voxygen.voxel.sprite.furniture.crate-2", + Vec3::new(-3.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 3), + "voxygen.voxel.sprite.furniture.crate-3", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 4), + "voxygen.voxel.sprite.furniture.crate-4", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 5), + "voxygen.voxel.sprite.furniture.crate-5", + Vec3::new(-5.5, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Crate, 6), + "voxygen.voxel.sprite.furniture.crate-6", + Vec3::new(-4.5, -3.0, 0.0), + Vec3::one(), + ), + // DrawerLarge + make_models( + (BlockKind::DrawerLarge, 0), + "voxygen.voxel.sprite.furniture.drawer_large-0", + Vec3::new(-11.5, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DrawerLarge, 1), + "voxygen.voxel.sprite.furniture.drawer_large-1", + Vec3::new(-11.5, -5.0, 0.0), + Vec3::one(), + ), + // DrawerMedium + make_models( + (BlockKind::DrawerMedium, 0), + "voxygen.voxel.sprite.furniture.drawer_medium-0", + Vec3::new(-11.0, -5.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DrawerMedium, 1), + "voxygen.voxel.sprite.furniture.drawer_medium-1", + Vec3::new(-11.0, -5.0, 0.0), + Vec3::one(), + ), + // DrawerSmall + make_models( + (BlockKind::DrawerSmall, 0), + "voxygen.voxel.sprite.furniture.drawer_small-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DrawerSmall, 1), + "voxygen.voxel.sprite.furniture.drawer_small-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // DungeonWallDecor + make_models( + (BlockKind::DungeonWallDecor, 0), + "voxygen.voxel.sprite.furniture.dungeon_wall-0", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 1), + "voxygen.voxel.sprite.furniture.dungeon_wall-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 2), + "voxygen.voxel.sprite.furniture.dungeon_wall-2", + Vec3::new(-5.5, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 3), + "voxygen.voxel.sprite.furniture.dungeon_wall-3", + Vec3::new(-1.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 4), + "voxygen.voxel.sprite.furniture.dungeon_wall-4", + Vec3::new(-5.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 5), + "voxygen.voxel.sprite.furniture.dungeon_wall-5", + Vec3::new(-5.5, -0.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 6), + "voxygen.voxel.sprite.furniture.dungeon_wall-6", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 7), + "voxygen.voxel.sprite.furniture.dungeon_wall-7", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 8), + "voxygen.voxel.sprite.furniture.dungeon_wall-8", + Vec3::new(-5.5, -1.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DungeonWallDecor, 9), + "voxygen.voxel.sprite.furniture.dungeon_wall-9", + Vec3::new(-1.5, -5.5, 0.0), + Vec3::one(), + ), + // HangingBasket + make_models( + (BlockKind::HangingBasket, 0), + "voxygen.voxel.sprite.furniture.hanging_basket-0", + Vec3::new(-6.5, -4.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::HangingBasket, 1), + "voxygen.voxel.sprite.furniture.hanging_basket-1", + Vec3::new(-9.5, -5.5, 0.0), + Vec3::one(), + ), + // HangingSign + make_models( + (BlockKind::HangingSign, 0), + "voxygen.voxel.sprite.furniture.hanging_sign-0", + Vec3::new(-3.5, -28.0, -4.0), + Vec3::one(), + ), + // WallLamp + make_models( + (BlockKind::WallLamp, 0), + "voxygen.voxel.sprite.furniture.lamp_wall-0", + Vec3::new(-6.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WallLamp, 1), + "voxygen.voxel.sprite.furniture.lamp_wall-1", + Vec3::new(-10.5, -9.0, 0.0), + Vec3::one(), + ), + // Planter + make_models( + (BlockKind::Planter, 0), + "voxygen.voxel.sprite.furniture.planter-0", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 1), + "voxygen.voxel.sprite.furniture.planter-1", + Vec3::new(-13.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 2), + "voxygen.voxel.sprite.furniture.planter-2", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 3), + "voxygen.voxel.sprite.furniture.planter-3", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 4), + "voxygen.voxel.sprite.furniture.planter-4", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 5), + "voxygen.voxel.sprite.furniture.planter-5", + Vec3::new(-6.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Planter, 6), + "voxygen.voxel.sprite.furniture.planter-6", + Vec3::new(-7.5, -3.5, 0.0), + Vec3::one(), + ), + //Pot + make_models( + (BlockKind::Pot, 0), + "voxygen.voxel.sprite.furniture.pot-0", + Vec3::new(-3.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Pot, 1), + "voxygen.voxel.sprite.furniture.pot-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Shelf + make_models( + (BlockKind::Shelf, 0), + "voxygen.voxel.sprite.furniture.shelf-0", + Vec3::new(-14.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Shelf, 1), + "voxygen.voxel.sprite.furniture.shelf-1", + Vec3::new(-13.5, -3.5, 0.0), + Vec3::one(), + ), + // TableSide + make_models( + (BlockKind::TableSide, 0), + "voxygen.voxel.sprite.furniture.table_side-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::TableSide, 1), + "voxygen.voxel.sprite.furniture.table_side-1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // TableDining + make_models( + (BlockKind::TableDining, 0), + "voxygen.voxel.sprite.furniture.table_dining-0", + Vec3::new(-8.5, -8.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::TableDining, 1), + "voxygen.voxel.sprite.furniture.table_dining-1", + Vec3::new(-8.5, -8.5, 0.0), + Vec3::one(), + ), + // TableDouble + make_models( + (BlockKind::TableDouble, 0), + "voxygen.voxel.sprite.furniture.table_double-0", + Vec3::new(-18.5, -11.5, 0.0), + Vec3::one(), + ), + // WardrobeSingle + make_models( + (BlockKind::WardrobeSingle, 0), + "voxygen.voxel.sprite.furniture.wardrobe_single-0", + Vec3::new(-5.5, -6.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WardrobeSingle, 1), + "voxygen.voxel.sprite.furniture.wardrobe_single-1", + Vec3::new(-5.5, -6.5, 0.0), + Vec3::one(), + ), + //WardrobeDouble + make_models( + (BlockKind::WardrobeDouble, 0), + "voxygen.voxel.sprite.furniture.wardrobe_double-0", + Vec3::new(-10.5, -6.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::WardrobeDouble, 1), + "voxygen.voxel.sprite.furniture.wardrobe_double-1", + Vec3::new(-10.5, -6.0, 0.0), + Vec3::one(), + ), + /* Stones */ + make_models( + (BlockKind::Stones, 0), + "voxygen.voxel.sprite.rocks.rock-0", + Vec3::new(-3.0, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Stones, 1), + "voxygen.voxel.sprite.rocks.rock-1", + Vec3::new(-4.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Stones, 2), + "voxygen.voxel.sprite.rocks.rock-2", + Vec3::new(-4.5, -4.5, 0.0), + Vec3::one(), + ), + /* Twigs */ + make_models( + (BlockKind::Twigs, 0), + "voxygen.voxel.sprite.twigs.twigs-0", + Vec3::new(-3.5, -3.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Twigs, 1), + "voxygen.voxel.sprite.twigs.twigs-1", + Vec3::new(-2.0, -1.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::Twigs, 2), + "voxygen.voxel.sprite.twigs.twigs-2", + Vec3::new(-4.0, -4.0, 0.0), + Vec3::one(), + ), + // Shiny Gems + make_models( + (BlockKind::ShinyGem, 0), + "voxygen.voxel.sprite.gem.gem_blue", + Vec3::new(-2.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShinyGem, 1), + "voxygen.voxel.sprite.gem.gem_green", + Vec3::new(-2.0, -3.0, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::ShinyGem, 2), + "voxygen.voxel.sprite.gem.gem_red", + Vec3::new(-3.0, -2.0, -2.0), + Vec3::one(), + ), + // Drop Gate Parts + make_models( + (BlockKind::DropGate, 0), + "voxygen.voxel.sprite.castle.drop_gate_bars-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::DropGateBottom, 0), + "voxygen.voxel.sprite.castle.drop_gate_bottom-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + // Snow covered Grass + make_models( + (BlockKind::GrassSnow, 0), + "voxygen.voxel.sprite.grass.grass_snow_0", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 1), + "voxygen.voxel.sprite.grass.grass_snow_1", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 2), + "voxygen.voxel.sprite.grass.grass_snow_2", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 3), + "voxygen.voxel.sprite.grass.grass_snow_3", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 4), + "voxygen.voxel.sprite.grass.grass_snow_4", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 5), + "voxygen.voxel.sprite.grass.grass_snow_5", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 6), + "voxygen.voxel.sprite.grass.grass_snow_6", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 7), + "voxygen.voxel.sprite.grass.grass_snow_7", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 8), + "voxygen.voxel.sprite.grass.grass_snow_8", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + make_models( + (BlockKind::GrassSnow, 9), + "voxygen.voxel.sprite.grass.grass_snow_9", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ] + .into_iter() + .collect(); + 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(), + shadow_chunks: Vec::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::BlueFlower, 7), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue-8", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 8), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue-9", - Vec3::new(-4.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::BlueFlower, 9), - make_models( - "voxygen.voxel.sprite.flowers.flower_blue-10", - Vec3::new(-1.5, -1.5, 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::PurpleFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-2", - Vec3::new(-5.0, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-3", - Vec3::new(-3.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-4", - Vec3::new(-5.0, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-5", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 5), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-6", - Vec3::new(-4.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 6), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-7", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::PurpleFlower, 7), - make_models( - "voxygen.voxel.sprite.flowers.flower_purple-8", - 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::RedFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_red-4", - Vec3::new(-6.5, -6.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::RedFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_red-5", - Vec3::new(-3.5, -3.5, 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::WhiteFlower, 2), - make_models( - "voxygen.voxel.sprite.flowers.flower_white-3", - Vec3::new(-1.5, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 3), - make_models( - "voxygen.voxel.sprite.flowers.flower_white-4", - Vec3::new(-5.0, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WhiteFlower, 4), - make_models( - "voxygen.voxel.sprite.flowers.flower_white-5", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::YellowFlower, 0), - make_models( - "voxygen.voxel.sprite.flowers.flower_yellow-1", - Vec3::new(-6.0, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::YellowFlower, 1), - make_models( - "voxygen.voxel.sprite.flowers.flower_yellow-0", - Vec3::new(-5.5, -5.5, 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::LargeGrass, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_large-0", - Vec3::new(-2.0, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_large-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::LongGrass, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_large-2", - Vec3::new(-5.5, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (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::Mushroom, 11), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-11", - Vec3::new(-8.0, -8.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 12), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-12", - Vec3::new(-5.0, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 13), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-13", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 14), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-14", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 15), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-15", - Vec3::new(-1.5, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Mushroom, 16), - make_models( - "voxygen.voxel.sprite.mushrooms.mushroom-16", - Vec3::new(-5.5, -5.5, 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(), - ), - ), - ( - (BlockKind::Fern, 12), - make_models( - "voxygen.voxel.sprite.ferns.fern-0", - Vec3::new(-6.5, -11.5, 0.0), - Vec3::unit_z(), - ), - ), - // 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(), - ), - ), - ( - (BlockKind::StreetLampTall, 0), - make_models( - "voxygen.voxel.sprite.furniture.street_lamp-0", - Vec3::new(-10.5, -10.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(), - ), - ), - // Bed - ( - (BlockKind::Bed, 0), - make_models( - "voxygen.voxel.sprite.furniture.bed-0", - Vec3::new(-9.5, -14.5, 0.0), - Vec3::one(), - ), - ), - // Bench - ( - (BlockKind::Bench, 0), - make_models( - "voxygen.voxel.sprite.furniture.bench-0", - Vec3::new(-14.0, -4.0, 0.0), - Vec3::one(), - ), - ), - // Chair - ( - (BlockKind::ChairSingle, 0), - make_models( - "voxygen.voxel.sprite.furniture.chair_single-0", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ChairSingle, 1), - make_models( - "voxygen.voxel.sprite.furniture.chair_single-1", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ChairDouble, 0), - make_models( - "voxygen.voxel.sprite.furniture.chair_double-0", - Vec3::new(-9.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ChairDouble, 1), - make_models( - "voxygen.voxel.sprite.furniture.chair_double-1", - Vec3::new(-9.5, -4.5, 0.0), - Vec3::one(), - ), - ), - // CoatRack - ( - (BlockKind::CoatRack, 0), - make_models( - "voxygen.voxel.sprite.furniture.coatrack-0", - Vec3::new(-6.5, -6.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::CoatRack, 1), - make_models( - "voxygen.voxel.sprite.furniture.coatrack-1", - Vec3::new(-6.5, -6.5, 0.0), - Vec3::one(), - ), - ), - // Crate - ( - (BlockKind::Crate, 0), - make_models( - "voxygen.voxel.sprite.furniture.crate-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 1), - make_models( - "voxygen.voxel.sprite.furniture.crate-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 2), - make_models( - "voxygen.voxel.sprite.furniture.crate-2", - Vec3::new(-3.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 3), - make_models( - "voxygen.voxel.sprite.furniture.crate-3", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 4), - make_models( - "voxygen.voxel.sprite.furniture.crate-4", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 5), - make_models( - "voxygen.voxel.sprite.furniture.crate-5", - Vec3::new(-5.5, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Crate, 6), - make_models( - "voxygen.voxel.sprite.furniture.crate-6", - Vec3::new(-4.5, -3.0, 0.0), - Vec3::one(), - ), - ), - // DrawerLarge - ( - (BlockKind::DrawerLarge, 0), - make_models( - "voxygen.voxel.sprite.furniture.drawer_large-0", - Vec3::new(-11.5, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DrawerLarge, 1), - make_models( - "voxygen.voxel.sprite.furniture.drawer_large-1", - Vec3::new(-11.5, -5.0, 0.0), - Vec3::one(), - ), - ), - // DrawerMedium - ( - (BlockKind::DrawerMedium, 0), - make_models( - "voxygen.voxel.sprite.furniture.drawer_medium-0", - Vec3::new(-11.0, -5.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DrawerMedium, 1), - make_models( - "voxygen.voxel.sprite.furniture.drawer_medium-1", - Vec3::new(-11.0, -5.0, 0.0), - Vec3::one(), - ), - ), - // DrawerSmall - ( - (BlockKind::DrawerSmall, 0), - make_models( - "voxygen.voxel.sprite.furniture.drawer_small-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DrawerSmall, 1), - make_models( - "voxygen.voxel.sprite.furniture.drawer_small-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // DungeonWallDecor - ( - (BlockKind::DungeonWallDecor, 0), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-0", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 1), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 2), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-2", - Vec3::new(-5.5, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 3), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-3", - Vec3::new(-1.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 4), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-4", - Vec3::new(-5.5, -4.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 5), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-5", - Vec3::new(-5.5, -0.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 6), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-6", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 7), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-7", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 8), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-8", - Vec3::new(-5.5, -1.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DungeonWallDecor, 9), - make_models( - "voxygen.voxel.sprite.furniture.dungeon_wall-9", - Vec3::new(-1.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // HangingBasket - ( - (BlockKind::HangingBasket, 0), - make_models( - "voxygen.voxel.sprite.furniture.hanging_basket-0", - Vec3::new(-6.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::HangingBasket, 1), - make_models( - "voxygen.voxel.sprite.furniture.hanging_basket-1", - Vec3::new(-9.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // HangingSign - ( - (BlockKind::HangingSign, 0), - make_models( - "voxygen.voxel.sprite.furniture.hanging_sign-0", - Vec3::new(-3.5, -28.0, -4.0), - Vec3::one(), - ), - ), - // WallLamp - ( - (BlockKind::WallLamp, 0), - make_models( - "voxygen.voxel.sprite.furniture.lamp_wall-0", - Vec3::new(-5.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WallLamp, 1), - make_models( - "voxygen.voxel.sprite.furniture.lamp_wall-1", - Vec3::new(-10.5, -9.0, 0.0), - Vec3::one(), - ), - ), - // Planter - ( - (BlockKind::Planter, 0), - make_models( - "voxygen.voxel.sprite.furniture.planter-0", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 1), - make_models( - "voxygen.voxel.sprite.furniture.planter-1", - Vec3::new(-13.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 2), - make_models( - "voxygen.voxel.sprite.furniture.planter-2", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 3), - make_models( - "voxygen.voxel.sprite.furniture.planter-3", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 4), - make_models( - "voxygen.voxel.sprite.furniture.planter-4", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 5), - make_models( - "voxygen.voxel.sprite.furniture.planter-5", - Vec3::new(-6.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Planter, 6), - make_models( - "voxygen.voxel.sprite.furniture.planter-6", - Vec3::new(-7.5, -3.5, 0.0), - Vec3::one(), - ), - ), - //Pot - ( - (BlockKind::Pot, 0), - make_models( - "voxygen.voxel.sprite.furniture.pot-0", - Vec3::new(-3.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Pot, 1), - make_models( - "voxygen.voxel.sprite.furniture.pot-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Shelf - ( - (BlockKind::Shelf, 0), - make_models( - "voxygen.voxel.sprite.furniture.shelf-0", - Vec3::new(-14.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Shelf, 1), - make_models( - "voxygen.voxel.sprite.furniture.shelf-1", - Vec3::new(-13.5, -3.5, 0.0), - Vec3::one(), - ), - ), - // TableSide - ( - (BlockKind::TableSide, 0), - make_models( - "voxygen.voxel.sprite.furniture.table_side-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::TableSide, 1), - make_models( - "voxygen.voxel.sprite.furniture.table_side-1", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // TableDining - ( - (BlockKind::TableDining, 0), - make_models( - "voxygen.voxel.sprite.furniture.table_dining-0", - Vec3::new(-8.5, -8.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::TableDining, 1), - make_models( - "voxygen.voxel.sprite.furniture.table_dining-1", - Vec3::new(-8.5, -8.5, 0.0), - Vec3::one(), - ), - ), - // TableDouble - ( - (BlockKind::TableDouble, 0), - make_models( - "voxygen.voxel.sprite.furniture.table_double-0", - Vec3::new(-18.5, -11.5, 0.0), - Vec3::one(), - ), - ), - // WardrobeSingle - ( - (BlockKind::WardrobeSingle, 0), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_single-0", - Vec3::new(-5.5, -6.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WardrobeSingle, 1), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_single-1", - Vec3::new(-5.5, -6.5, 0.0), - Vec3::one(), - ), - ), - //WardrobeDouble - ( - (BlockKind::WardrobeDouble, 0), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_double-0", - Vec3::new(-10.5, -6.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::WardrobeDouble, 1), - make_models( - "voxygen.voxel.sprite.furniture.wardrobe_double-1", - Vec3::new(-10.5, -6.0, 0.0), - Vec3::one(), - ), - ), - /* Stones */ - ( - (BlockKind::Stones, 0), - make_models( - "voxygen.voxel.sprite.rocks.rock-0", - Vec3::new(-3.0, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Stones, 1), - make_models( - "voxygen.voxel.sprite.rocks.rock-1", - Vec3::new(-4.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Stones, 2), - make_models( - "voxygen.voxel.sprite.rocks.rock-2", - Vec3::new(-4.5, -4.5, 0.0), - Vec3::one(), - ), - ), - /* Twigs */ - ( - (BlockKind::Twigs, 0), - make_models( - "voxygen.voxel.sprite.twigs.twigs-0", - Vec3::new(-3.5, -3.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Twigs, 1), - make_models( - "voxygen.voxel.sprite.twigs.twigs-1", - Vec3::new(-2.0, -1.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::Twigs, 2), - make_models( - "voxygen.voxel.sprite.twigs.twigs-2", - Vec3::new(-4.0, -4.0, 0.0), - Vec3::one(), - ), - ), - // Shiny Gems - ( - (BlockKind::ShinyGem, 0), - make_models( - "voxygen.voxel.sprite.gem.gem_blue", - Vec3::new(-2.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShinyGem, 1), - make_models( - "voxygen.voxel.sprite.gem.gem_green", - Vec3::new(-2.0, -3.0, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::ShinyGem, 2), - make_models( - "voxygen.voxel.sprite.gem.gem_red", - Vec3::new(-3.0, -2.0, -2.0), - Vec3::one(), - ), - ), - // Drop Gate Parts - ( - (BlockKind::DropGate, 0), - make_models( - "voxygen.voxel.sprite.castle.drop_gate_bars-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::DropGateBottom, 0), - make_models( - "voxygen.voxel.sprite.castle.drop_gate_bottom-0", - Vec3::new(-5.5, -5.5, 0.0), - Vec3::one(), - ), - ), - // Snow covered Grass - ( - (BlockKind::GrassSnow, 0), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_0", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 1), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_1", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 2), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_2", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 3), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_3", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 4), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_4", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 5), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_5", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 6), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_6", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 7), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_7", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 8), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_8", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ( - (BlockKind::GrassSnow, 9), - make_models( - "voxygen.voxel.sprite.grass.grass_snow_9", - Vec3::new(-2.5, -2.5, 0.0), - Vec3::one(), - ), - ), - ] - .into_iter() - .collect(), + sprite_data: Arc::new(sprite_data), + sprite_col_lights, waves: renderer .create_texture( &assets::load_expect("voxygen.texture.waves"), Some(gfx::texture::FilterMethod::Trilinear), Some(gfx::texture::WrapMode::Tile), + 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 = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { + // TODO: Verify some good empirical constants. + small_size_threshold: 128, + large_size_threshold: 1024, + ..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::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, + ), + )?; + Ok((atlas, texture)) + } + + fn remove_chunk_meta(&mut self, _pos: Vec2, chunk: &TerrainChunkData) { + 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) { + if let Some(chunk) = self.chunks.remove(&pos) { + self.remove_chunk_meta(pos, &chunk); + // Temporarily remember dead chunks for shadowing purposes. + self.shadow_chunks.push((pos, chunk)); + } + if let Some(_todo) = self.mesh_todo.remove(&pos) { + //Do nothing on todo mesh removal. + } + } + /// Maintain terrain data. To be called once per tick. #[allow(clippy::for_loops_over_fallibles)] // TODO: Pending review in #587 #[allow(clippy::len_zero)] // TODO: Pending review in #587 @@ -2869,9 +2472,10 @@ impl Terrain { loaded_distance: f32, view_mat: Mat4, proj_mat: Mat4, - ) { + ) -> (Aabb, Vec>, math::Aabr) { 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. @@ -2964,11 +2568,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() @@ -3000,10 +2606,13 @@ impl Terrain { // a sample of the terrain that includes both the chunk we want and // its neighbours. let volume = match scene_data.state.terrain().sample(aabr) { - Ok(sample) => sample, + Ok(sample) => sample, /* TODO: Ensure that all of the chunk's neighbours still + * 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"), }; @@ -3026,13 +2635,17 @@ impl Terrain { // Queue the worker thread. let started_tick = todo.started_tick; + let sprite_data = Arc::clone(&self.sprite_data); scene_data.thread_pool.execute(move || { + let sprite_data = sprite_data; let _ = send.send(mesh_worker( pos, (min_z as f32, max_z as f32), started_tick, volume, + max_texture_size, aabb, + &sprite_data, )); }); todo.active_worker = Some(todo.started_tick); @@ -3047,12 +2660,36 @@ impl Terrain { // 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; 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; + 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."); + // 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 let Err(err) = renderer.update_texture( + &self.col_lights, + atlas_offs.into_array(), + tex_size.into_array(), + &tex, + ) { + warn!("Failed to update texture: {:?}", err); + } + + self.insert_chunk(response.pos, TerrainChunkData { load_time, opaque_model: renderer .create_model(&response.opaque_mesh) @@ -3066,6 +2703,7 @@ impl Terrain { } else { None }, + col_lights: allocation.id, sprite_instances: response .sprite_instances .into_iter() @@ -3086,40 +2724,55 @@ 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, + 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 { 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) => {}, + None => {}, } } // Construct view frustum - let frustum = Frustum::from_modelview_projection((proj_mat * view_mat).into_col_arrays()); + 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; for (pos, chunk) in &mut self.chunks { - let chunk_pos = pos.map(|e| e as f32 * chunk_sz); + let chunk_pos = pos.as_::() * 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); - let in_range = Vec2::::from(focus_pos).distance_squared(nearest_in_chunk) - < loaded_distance.powf(2.0); + let distance_2 = Vec2::::from(focus_pos).distance_squared(nearest_in_chunk); + let in_range = distance_2 < loaded_distance.powf(2.0); if !in_range { - chunk.visible = in_range; + chunk.visible = Visibility::OutOfRange; continue; } @@ -3135,52 +2788,237 @@ 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), + }; + + if in_frustum { + let visible_box = chunk_box; + visible_bounding_box = visible_bounding_box + .map(|e| e.union(visible_box)) + .or(Some(visible_box)); + } + // 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_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, + max: focus_pos + 2.0, + }); + + // PSCs: Potential shadow casters + let ray_direction = scene_data.get_sun_dir(); + let collides_with_aabr = |a: math::Aabb, b: math::Aabr| { + let min = math::Vec4::new(a.min.x, a.min.y, b.min.x, b.min.y); + let max = math::Vec4::new(b.max.x, b.max.y, a.max.x, a.max.y); + min.partial_cmple_simd(max).reduce_and() + }; + let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0 + && renderer.render_mode().shadow.is_map() + { + let visible_bounding_box = math::Aabb:: { + min: math::Vec3::from(visible_bounding_box.min - focus_off), + max: math::Vec3::from(visible_bounding_box.max - focus_off), + }; + let focus_off = math::Vec3::from(focus_off); + let visible_bounds_fine = visible_bounding_box.as_::(); + let inv_proj_view = + math::Mat4::from_col_arrays((proj_mat * view_mat).into_col_arrays()) + .as_::() + .inverted(); + let ray_direction = math::Vec3::::from(ray_direction); + let visible_light_volume = math::calc_focused_light_volume_points( + inv_proj_view, + ray_direction.as_::(), + visible_bounds_fine, + 1e-6, + ) + .map(|v| v.as_::()) + .collect::>(); + + let cam_pos = math::Vec4::from(view_mat.inverted() * Vec4::unit_w()).xyz(); + let up: math::Vec3 = { math::Vec3::up() }; + + let ray_mat = math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, up); + let visible_bounds = math::Aabr::from(math::fit_psr( + ray_mat, + visible_light_volume.iter().copied(), + |p| p, + )); + let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off); + + let can_shadow_sun = |pos: Vec2, chunk: &TerrainChunkData| { + let chunk_pos = pos.as_::() * chunk_sz; + + // Ensure the chunk is within the PSR set. + let chunk_box = math::Aabb { + min: math::Vec3::new(chunk_pos.x, chunk_pos.y, chunk.z_bounds.0), + max: math::Vec3::new( + chunk_pos.x + chunk_sz, + chunk_pos.y + chunk_sz, + chunk.z_bounds.1, + ), + }; + + let chunk_from_light = math::fit_psr( + ray_mat, + math::aabb_to_points(chunk_box).iter().copied(), + |p| p, + ); + collides_with_aabr(chunk_from_light, visible_bounds) + }; + + // Handle potential shadow casters (chunks that aren't visible, but are still in + // range) to see if they could cast shadows. + 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)| { + chunk.can_shadow_sun = can_shadow_sun(pos, chunk); + }); + + // Handle dead chunks that we kept around only to make sure shadows don't blink + // out when a chunk disappears. + // + // If the sun can currently cast shadows, we retain only those shadow chunks + // that both: 1. have not been replaced by a real chunk instance, + // and 2. are currently potential shadow casters (as witnessed by + // `can_shadow_sun` returning true). + // + // NOTE: Please make sure this runs *after* any code that could insert a chunk! + // Otherwise we may end up with multiple instances of the chunk trying to cast + // shadows at the same time. + let chunks = &self.chunks; + self.shadow_chunks + .retain(|(pos, chunk)| !chunks.contains_key(pos) && can_shadow_sun(*pos, chunk)); + + (visible_light_volume, visible_bounds) + } else { + // There's no daylight or no shadows, so there's no reason to keep any + // shadow chunks around. + self.shadow_chunks.clear(); + (Vec::new(), math::Aabr { + min: math::Vec2::zero(), + max: math::Vec2::zero(), + }) + }; + + ( + visible_bounding_box, + visible_light_volume, + visible_psr_bounds, + ) } 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 shadow_chunk_count(&self) -> usize { self.shadow_chunks.len() } + + pub fn render_shadows( + &self, + renderer: &mut Renderer, + global: &GlobalModel, + (is_daylight, light_data): super::LightData, + focus_pos: Vec3, + ) { + if !renderer.render_mode().shadow.is_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) + }) + .take(self.chunks.len()); + + // Directed shadows + // + // NOTE: We also render shadows for dead chunks that were found to still be + // potential shadow casters, to avoid shadows suddenly disappearing at + // very steep sun angles (e.g. sunrise / sunset). + if is_daylight { + chunk_iter + .clone() + .filter(|chunk| chunk.can_shadow_sun()) + .chain(self.shadow_chunks.iter().map(|(_, chunk)| chunk)) + .for_each(|chunk| { + // Directed light shadows. + renderer.render_terrain_shadow_directed( + &chunk.opaque_model, + global, + &chunk.locals, + &global.shadow_mats, + ); + }); + } + + // Point shadows + // + // NOTE: We don't bother retaining chunks unless they cast sun shadows, so we + // don't use `shadow_chunks` here. + light_data.iter().take(1).for_each(|_light| { + chunk_iter.clone().for_each(|chunk| { + if chunk.can_shadow_point { + renderer.render_shadow_point( + &chunk.opaque_model, + global, + &chunk.locals, + &global.shadow_mats, + ); + } + }); + }); } pub fn render( &self, renderer: &mut Renderer, - globals: &Consts, - lights: &Consts, - shadows: &Consts, + global: &GlobalModel, + lod: &LodData, focus_pos: Vec3, ) { let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) }); - let chunks = &self.chunks; let chunk_iter = Spiral2d::new() - .scan(0, |n, rpos| { - if *n >= chunks.len() { - None - } else { - let pos = focus_chunk + rpos; - Some(chunks.get(&pos).map(|c| { - *n += 1; - (pos, c) - })) - } + .filter_map(|rpos| { + let pos = focus_chunk + rpos; + self.chunks.get(&pos).map(|c| (pos, c)) }) - .filter_map(|x| x); + .take(self.chunks.len()); - // Opaque - for (_, chunk) in chunk_iter.clone() { - if chunk.visible { + for (_, chunk) in chunk_iter { + if chunk.visible == Visibility::Visible { renderer.render_terrain_chunk( &chunk.opaque_model, - globals, + &self.col_lights, + global, &chunk.locals, - lights, - shadows, + lod, ); } } @@ -3189,65 +3027,76 @@ impl Terrain { pub fn render_translucent( &self, renderer: &mut Renderer, - globals: &Consts, - lights: &Consts, - shadows: &Consts, + global: &GlobalModel, + 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) }); - let chunks = &self.chunks; + // Avoid switching textures let chunk_iter = Spiral2d::new() - .scan(0, |n, rpos| { - if *n >= chunks.len() { - None - } else { - let pos = focus_chunk + rpos; - Some(chunks.get(&pos).map(|c| { - *n += 1; - (pos, c) - })) - } + .filter_map(|rpos| { + let pos = focus_chunk + rpos; + self.chunks.get(&pos).map(|c| (pos, c)) }) - .filter_map(|x| x); + .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 { - if let Some(models) = self.sprite_models.get(&kind) { - renderer.render_sprites( - if dist_sqrd < sprite_high_detail_distance.powf(2.0) { - &models[0] - } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { - &models[1] - } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { - &models[2] - } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { - &models[3] - } else { - &models[4] - }, - globals, - &instances, - lights, - shadows, - ); + 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) in (&chunk.sprite_instances).into_iter() { + 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_data[&kind][0] + } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { + &self.sprite_data[&kind][1] + } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { + &self.sprite_data[&kind][2] + } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { + &self.sprite_data[&kind][3] } else { - warn!("Sprite model for {:?} does not exists", kind); - } + &self.sprite_data[&kind][4] + }; + renderer.render_sprites( + model, + &self.sprite_col_lights, + global, + &chunk.locals, + locals, + &instances, + lod, + ); } } } @@ -3256,7 +3105,7 @@ impl Terrain { // Translucent chunk_iter .clone() - .filter(|(_, chunk)| chunk.visible) + .filter(|(_, chunk)| chunk.visible == Visibility::Visible) .filter_map(|(_, chunk)| { chunk .fluid_model @@ -3267,7 +3116,13 @@ impl Terrain { .into_iter() .rev() // Render back-to-front .for_each(|(model, locals)| { - renderer.render_fluid_chunk(model, globals, locals, lights, shadows, &self.waves) + renderer.render_fluid_chunk( + model, + global, + locals, + lod, + &self.waves, + ) }); } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index dc43964b3d..a12398c265 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -63,7 +63,11 @@ impl SessionState { pub fn new(global_state: &mut GlobalState, client: Rc>) -> Self { // Create a scene for this session. The scene handles visible elements of the // game world. - let mut scene = Scene::new(global_state.window.renderer_mut()); + let mut scene = Scene::new( + global_state.window.renderer_mut(), + &*client.borrow(), + &global_state.settings, + ); scene .camera_mut() .set_fov_deg(global_state.settings.graphics.fov); @@ -218,6 +222,9 @@ impl PlayState for SessionState { let camera::Dependents { cam_pos, cam_dir, .. } = 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; let (is_aiming, aim_dir_offset) = { let client = self.client.borrow(); @@ -677,11 +684,13 @@ impl PlayState for SessionState { .camera_mut() .compute_dependents(&*self.client.borrow().state().terrain()); - // Extract HUD events ensuring the client borrow gets dropped. - let mut hud_events = self.hud.maintain( - &self.client.borrow(), - global_state, - DebugInfo { + // Generate debug info, if needed (it iterates through enough data that we might + // as well avoid it unless we need it). + let debug_info = global_state + .settings + .gameplay + .toggle_debug + .then(|| DebugInfo { tps: global_state.clock.get_tps(), ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), coordinates: self @@ -709,13 +718,21 @@ impl PlayState for SessionState { .get(self.client.borrow().entity()) .cloned(), num_chunks: self.scene.terrain().chunk_count() as u32, + num_lights: self.scene.lights().len() as u32, num_visible_chunks: self.scene.terrain().visible_chunk_count() as u32, + num_shadow_chunks: self.scene.terrain().shadow_chunk_count() as u32, num_figures: self.scene.figure_mgr().figure_count() as u32, num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32, num_particles: self.scene.particle_mgr().particle_count() as u32, num_particles_visible: self.scene.particle_mgr().particle_count_visible() as u32, - }, + }); + + // Extract HUD events ensuring the client borrow gets dropped. + let mut hud_events = self.hud.maintain( + &self.client.borrow(), + global_state, + &debug_info, &self.scene.camera(), global_state.clock.get_last_delta(), HudInfo { @@ -731,7 +748,9 @@ impl PlayState for SessionState { // Look for changes in the localization files if global_state.localization_watcher.reloaded() { - hud_events.push(HudEvent::ChangeLanguage(self.voxygen_i18n.metadata.clone())); + hud_events.push(HudEvent::ChangeLanguage(Box::new( + self.voxygen_i18n.metadata.clone(), + ))); } // Maintain the UI. @@ -806,6 +825,12 @@ impl PlayState for SessionState { global_state.settings.graphics.view_distance = view_distance; global_state.settings.save_to_file_warn(); }, + HudEvent::AdjustLodDetail(lod_detail) => { + self.scene.lod.set_detail(lod_detail); + + global_state.settings.graphics.lod_detail = lod_detail; + global_state.settings.save_to_file_warn(); + }, HudEvent::AdjustSpriteRenderDistance(sprite_render_distance) => { global_state.settings.graphics.sprite_render_distance = sprite_render_distance; @@ -919,34 +944,14 @@ 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 :) global_state .window .renderer_mut() - .set_aa_mode(new_aa_mode) + .set_render_mode((&*new_render_mode).clone()) .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::ChangeResolution(new_resolution) => { @@ -1170,7 +1175,7 @@ fn under_cursor( let cam_dist = cam_ray.0; // The ray hit something, is it within range? - let (build_pos, select_pos) = if matches!(cam_ray.1, Ok(Some(_)) if + let (build_pos, select_pos) = if matches!(cam_ray.1, Ok(Some(_)) if player_pos.distance_squared(cam_pos + cam_dir * cam_dist) <= MAX_PICKUP_RANGE_SQR) { diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index b953960231..bca5eec04e 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}, + render::RenderMode, ui::ScaleMode, window::{GameInput, KeyMouse}, }; @@ -616,14 +616,13 @@ 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 render_mode: RenderMode, pub resolution: [u16; 2], pub bit_depth: Option, pub refresh_rate: Option, pub window_size: [u16; 2], pub fullscreen: bool, + pub lod_detail: u32, } impl Default for GraphicsSettings { @@ -636,17 +635,17 @@ impl Default for GraphicsSettings { max_fps: 60, fov: 50, gamma: 1.0, - aa_mode: AaMode::Fxaa, - cloud_mode: CloudMode::Regular, - fluid_mode: FluidMode::Shiny, + render_mode: RenderMode::default(), resolution: [1920, 1080], bit_depth: None, refresh_rate: None, window_size: [1920, 1080], fullscreen: false, + lod_detail: 300, } } } + #[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..fe2eaa13f5 100644 --- a/voxygen/src/ui/cache.rs +++ b/voxygen/src/ui/cache.rs @@ -1,18 +1,23 @@ use super::graphic::{Graphic, GraphicCache, Id as GraphicId}; use crate::{ - render::{Renderer, Texture}, + render::{Mesh, Renderer, Texture, UiPipeline}, Error, }; -use conrod_core::text::GlyphCache; +use conrod_core::{text::GlyphCache, widget::Id}; +use hashbrown::HashMap; use vek::*; // Multiplied by current window size const GLYPH_CACHE_SIZE: u16 = 1; // Glyph cache tolerances -const SCALE_TOLERANCE: f32 = 0.1; -const POSITION_TOLERANCE: f32 = 0.1; +const SCALE_TOLERANCE: f32 = 0.5; +const POSITION_TOLERANCE: f32 = 0.5; + +type TextCache = HashMap>; pub struct Cache { + // Map from text ids to their positioned glyphs. + text_cache: TextCache, glyph_cache: GlyphCache<'static>, glyph_cache_tex: Texture, graphic_cache: GraphicCache, @@ -26,9 +31,10 @@ 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 { + text_cache: Default::default(), glyph_cache: GlyphCache::builder() .dimensions(glyph_cache_dims.x as u32, glyph_cache_dims.y as u32) .scale_tolerance(SCALE_TOLERANCE) @@ -41,14 +47,24 @@ impl Cache { pub fn glyph_cache_tex(&self) -> &Texture { &self.glyph_cache_tex } - pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture) { - (&mut self.glyph_cache, &self.glyph_cache_tex) + pub fn cache_mut_and_tex( + &mut self, + ) -> ( + &mut GraphicCache, + &mut TextCache, + &mut GlyphCache<'static>, + &Texture, + ) { + ( + &mut self.graphic_cache, + &mut self.text_cache, + &mut self.glyph_cache, + &self.glyph_cache_tex, + ) } pub fn graphic_cache(&self) -> &GraphicCache { &self.graphic_cache } - pub fn graphic_cache_mut(&mut self) -> &mut GraphicCache { &mut self.graphic_cache } - pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId { self.graphic_cache.add_graphic(graphic) } @@ -57,17 +73,17 @@ impl Cache { self.graphic_cache.replace_graphic(id, graphic) } - // Resizes and clears the GraphicCache - pub fn resize_graphic_cache(&mut self, renderer: &mut Renderer) { + /// Resizes and clears the various caches. + /// + /// To be called when something like the scaling factor changes, + /// invalidating all existing cached UI state. + pub fn resize(&mut self, renderer: &mut Renderer) -> Result<(), Error> { self.graphic_cache.clear_cache(renderer); - } - - // Resizes and clears the GlyphCache - pub fn resize_glyph_cache(&mut self, renderer: &mut Renderer) -> Result<(), Error> { + self.text_cache.clear(); 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 aa696be782..171d4156ab 100644 --- a/voxygen/src/ui/graphic/mod.rs +++ b/voxygen/src/ui/graphic/mod.rs @@ -3,7 +3,7 @@ mod renderer; pub use renderer::{SampleStrat, Transform}; -use crate::render::{Renderer, Texture}; +use crate::render::{RenderError, Renderer, Texture}; use common::figure::Segment; use guillotiere::{size2, SimpleAtlasAllocator}; use hashbrown::{hash_map::Entry, HashMap}; @@ -15,7 +15,14 @@ use vek::*; #[derive(Clone)] pub enum Graphic { - Image(Arc), + /// NOTE: The second argument is an optional border color. If this is set, + /// we force the image into its own texture and use the border color + /// whenever we sample beyond the image extent. This can be useful, for + /// example, for the map and minimap, which both rotate and may be + /// non-square (meaning if we want to display the whole map and render to a + /// square, we may render out of bounds unless we perform proper + /// clipping). + Image(Arc, Option>), // Note: none of the users keep this Arc currently Voxel(Arc, Transform, SampleStrat), Blank, @@ -53,20 +60,73 @@ pub struct TexId(usize); type Parameters = (Id, Vec2); type GraphicMap = HashMap; -enum CacheLoc { +enum CachedDetails { Atlas { // Index of the atlas this is cached in atlas_idx: usize, + // Whether this texture is valid. + valid: bool, // Where in the cache texture this is aabr: Aabr, }, Texture { + // Index of the (unique, non-atlas) texture this is cached in. + index: usize, + // Whether this texture is valid. + valid: bool, + }, + Immutable { + // Index of the (unique, immutable, non-atlas) texture this is cached in. index: usize, }, } -struct CachedDetails { - location: CacheLoc, - valid: bool, + +impl CachedDetails { + /// Get information about this cache entry: texture index, + /// whether the entry is valid, and its bounding box in the referenced + /// texture. + fn info( + &self, + atlases: &[(SimpleAtlasAllocator, usize)], + dims: Vec2, + ) -> (usize, bool, Aabr) { + match *self { + CachedDetails::Atlas { + atlas_idx, + valid, + aabr, + } => (atlases[atlas_idx].1, valid, aabr), + CachedDetails::Texture { index, valid } => { + (index, valid, Aabr { + min: Vec2::zero(), + // Note texture should always match the cached dimensions + max: dims, + }) + }, + CachedDetails::Immutable { index } => { + (index, true, Aabr { + min: Vec2::zero(), + // Note texture should always match the cached dimensions + max: dims, + }) + }, + } + } + + /// Attempt to invalidate this cache entry. + pub fn invalidate(&mut self) -> Result<(), ()> { + match self { + Self::Atlas { ref mut valid, .. } => { + *valid = false; + Ok(()) + }, + Self::Texture { ref mut valid, .. } => { + *valid = false; + Ok(()) + }, + Self::Immutable { .. } => Err(()), + } + } } // Caches graphics, only deallocates when changing screen resolution (completely @@ -106,22 +166,18 @@ impl GraphicCache { } pub fn replace_graphic(&mut self, id: Id, graphic: Graphic) { - self.graphic_map.insert(id, graphic); + if self.graphic_map.insert(id, graphic).is_none() { + // This was not an update, so no need to search for keys. + return; + } // Remove from caches // Maybe make this more efficient if replace graphic is used more often - let uses = self - .cache_map - .keys() - .filter(|k| k.0 == id) - .copied() - .collect::>(); - for p in uses { - if let Some(details) = self.cache_map.get_mut(&p) { - // Reuse allocation - details.valid = false; - } - } + self.cache_map.retain(|&(key_id, _key_dims), details| { + // If the entry does not reference id, or it does but we can successfully + // invalidate, retain the entry; otherwise, discard this entry completely. + key_id != id || details.invalidate().is_ok() + }); } pub fn get_graphic(&self, id: Id) -> Option<&Graphic> { self.graphic_map.get(&id) } @@ -182,29 +238,31 @@ impl GraphicCache { // TODO: Verify rotation is being applied correctly. let transformed_aabr = |aabr| rotated_aabr(scaled_aabr(aabr)); - let details = match self.cache_map.entry(key) { + let Self { + textures, + atlases, + cache_map, + graphic_map, + .. + } = self; + + let details = match cache_map.entry(key) { Entry::Occupied(details) => { let details = details.get(); - let (idx, aabr) = match details.location { - CacheLoc::Atlas { - atlas_idx, aabr, .. - } => (self.atlases[atlas_idx].1, aabr), - CacheLoc::Texture { index } => { - (index, Aabr { - min: Vec2::new(0, 0), - // Note texture should always match the cached dimensions - max: dims, - }) - }, - }; + let (idx, valid, aabr) = details.info(atlases, dims); // Check if the cached version has been invalidated by replacing the underlying // graphic - if !details.valid { + if !valid { // Create image - let image = draw_graphic(&self.graphic_map, graphic_id, dims)?; + let (image, border) = draw_graphic(graphic_map, graphic_id, dims)?; + // If the cache location is invalid, we know the underlying texture is mutable, + // so we should be able to replace the graphic. However, we still want to make + // sure that we are not reusing textures for images that specify a border + // color. + assert!(border.is_none()); // Transfer to the gpu - upload_image(renderer, aabr, &self.textures[idx], &image); + upload_image(renderer, aabr, &textures[idx], &image); } return Some((transformed_aabr(aabr.map(|e| e as f64)), TexId(idx))); @@ -212,24 +270,39 @@ impl GraphicCache { Entry::Vacant(details) => details, }; - // Create image - let image = draw_graphic(&self.graphic_map, graphic_id, dims)?; + // Construct image + let (image, border_color) = draw_graphic(graphic_map, graphic_id, dims)?; + + // Upload + let atlas_size = atlas_size(renderer); // Allocate space on the gpu // Check size of graphic // Graphics over a particular size are sent to their own textures - let location = if Vec2::::from(self.atlases[0].0.size().to_tuple()) - .map(|e| e as u16) + let location = if let Some(border_color) = border_color { + // Create a new immutable texture. + let texture = create_image(renderer, image, border_color).unwrap(); + // NOTE: All mutations happen only after the upload succeeds! + let index = textures.len(); + textures.push(texture); + CachedDetails::Immutable { index } + } else if atlas_size .map2(dims, |a, d| a as f32 * ATLAS_CUTTOFF_FRAC >= d as f32) .reduce_and() { // Fit into an atlas let mut loc = None; - for (atlas_idx, (ref mut atlas, _)) in self.atlases.iter_mut().enumerate() { + for (atlas_idx, &mut (ref mut atlas, texture_idx)) in 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); - loc = Some(CacheLoc::Atlas { atlas_idx, aabr }); + loc = Some(CachedDetails::Atlas { + atlas_idx, + valid: true, + aabr, + }); + upload_image(renderer, aabr, &textures[texture_idx], &image); break; } } @@ -239,65 +312,70 @@ 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) .unwrap(); - let tex_idx = self.textures.len(); - let atlas_idx = self.atlases.len(); - self.textures.push(texture); - self.atlases.push((atlas, tex_idx)); - CacheLoc::Atlas { atlas_idx, aabr } + // NOTE: All mutations happen only after the texture creation succeeds! + let tex_idx = textures.len(); + let atlas_idx = atlases.len(); + textures.push(texture); + atlases.push((atlas, tex_idx)); + upload_image(renderer, aabr, &textures[tex_idx], &image); + CachedDetails::Atlas { + atlas_idx, + valid: true, + aabr, + } }, } } else { // Create a texture just for this let texture = renderer.create_dynamic_texture(dims).unwrap(); - let index = self.textures.len(); - self.textures.push(texture); - CacheLoc::Texture { index } - }; - - let (idx, aabr) = match location { - CacheLoc::Atlas { - atlas_idx, aabr, .. - } => (self.atlases[atlas_idx].1, aabr), - CacheLoc::Texture { index } => { - (index, Aabr { - min: Vec2::new(0, 0), + // NOTE: All mutations happen only after the texture creation succeeds! + let index = textures.len(); + textures.push(texture); + upload_image( + renderer, + Aabr { + min: Vec2::zero(), // Note texture should always match the cached dimensions max: dims, - }) - }, + }, + &textures[index], + &image, + ); + CachedDetails::Texture { index, valid: true } }; - // Upload - upload_image(renderer, aabr, &self.textures[idx], &image); + + // Extract information from cache entry. + let (idx, _, aabr) = location.info(atlases, dims); + // Insert into cached map - details.insert(CachedDetails { - location, - valid: true, - }); + details.insert(location); Some((transformed_aabr(aabr.map(|e| e as f64)), TexId(idx))) } } // Draw a graphic at the specified dimensions -fn draw_graphic(graphic_map: &GraphicMap, graphic_id: Id, dims: Vec2) -> Option { +fn draw_graphic( + graphic_map: &GraphicMap, + graphic_id: Id, + dims: Vec2, +) -> Option<(RgbaImage, Option>)> { match graphic_map.get(&graphic_id) { Some(Graphic::Blank) => None, // Render image at requested resolution // TODO: Use source aabr. - Some(Graphic::Image(ref image)) => Some(resize_pixel_art( - &image.to_rgba(), - u32::from(dims.x), - u32::from(dims.y), + Some(&Graphic::Image(ref image, border_color)) => Some(( + resize_pixel_art(&image.to_rgba(), u32::from(dims.x), u32::from(dims.y)), + border_color, )), - Some(Graphic::Voxel(ref segment, trans, sample_strat)) => Some(renderer::draw_vox( - &segment, - dims, - trans.clone(), - *sample_strat, + Some(Graphic::Voxel(ref segment, trans, sample_strat)) => Some(( + renderer::draw_vox(&segment, dims, trans.clone(), *sample_strat), + None, )), None => { warn!( @@ -309,17 +387,18 @@ fn draw_graphic(graphic_map: &GraphicMap, graphic_id: Id, dims: Vec2) -> Op } } -fn create_atlas_texture(renderer: &mut Renderer) -> (SimpleAtlasAllocator, Texture) { - let (w, h) = renderer.get_resolution().into_tuple(); - +fn atlas_size(renderer: &Renderer) -> Vec2 { let max_texture_size = renderer.max_texture_size(); - let size = Vec2::new(w, h).map(|e| { + renderer.get_resolution().map(|e| { (e * GRAPHIC_CACHE_RELATIVE_SIZE) .max(512) - .min(max_texture_size as u16) - }); + .min(max_texture_size) + }) +} +fn create_atlas_texture(renderer: &mut Renderer) -> (SimpleAtlasAllocator, Texture) { + let size = atlas_size(renderer); let atlas = SimpleAtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))); let texture = renderer.create_dynamic_texture(size).unwrap(); (atlas, texture) @@ -340,8 +419,23 @@ fn upload_image(renderer: &mut Renderer, aabr: Aabr, tex: &Texture, image: tex, offset, size, - &image.pixels().map(|p| p.0).collect::>(), + // NOTE: Rgba texture, so each pixel is 4 bytes, ergo this cannot fail. + // We make the cast parameters explicit for clarity. + gfx::memory::cast_slice::(&image), ) { warn!(?e, "Failed to update texture"); } } + +fn create_image( + renderer: &mut Renderer, + image: RgbaImage, + border_color: Rgba, +) -> Result { + renderer.create_texture( + &DynamicImage::ImageRgba8(image), + None, + Some(gfx::texture::WrapMode::Border), + Some(border_color.into_array().into()), + ) +} diff --git a/voxygen/src/ui/graphic/pixel_art.rs b/voxygen/src/ui/graphic/pixel_art.rs index f3994b4375..fa32f569fd 100644 --- a/voxygen/src/ui/graphic/pixel_art.rs +++ b/voxygen/src/ui/graphic/pixel_art.rs @@ -47,7 +47,7 @@ pub fn resize_pixel_art(image: &RgbaImage, new_width: u32, new_height: u32) -> R for x in 0..new_width { // Calculate sampling strategy let xsmin = x as f32 * wratio; - let xsmax = xsmin + wratio; + let xsmax = (xsmin + wratio).min(width as f32); // Min and max pixels covered let xminp = xsmin.floor() as u32; let xmaxp = ((xsmax - EPSILON).ceil() as u32) @@ -63,7 +63,7 @@ pub fn resize_pixel_art(image: &RgbaImage, new_width: u32, new_height: u32) -> R for y in 0..new_height { // Calculate sampling strategy let ysmin = y as f32 * hratio; - let ysmax = ysmin + hratio; + let ysmax = (ysmin + hratio).min(height as f32); // Min and max of pixels covered let yminp = ysmin.floor() as u32; let ymaxp = ((ysmax - EPSILON).ceil() as u32) diff --git a/voxygen/src/ui/graphic/renderer.rs b/voxygen/src/ui/graphic/renderer.rs index 697a17a0f3..a456c8936b 100644 --- a/voxygen/src/ui/graphic/renderer.rs +++ b/voxygen/src/ui/graphic/renderer.rs @@ -199,7 +199,7 @@ pub fn draw_vox( .resize_exact( output_size.x as u32, output_size.y as u32, - image::FilterType::Triangle, + image::imageops::FilterType::Triangle, ) .to_rgba(), SampleStrat::PixelCoverage => super::pixel_art::resize_pixel_art( diff --git a/voxygen/src/ui/img_ids.rs b/voxygen/src/ui/img_ids.rs index 2e64e904f7..e0b8ec3a37 100644 --- a/voxygen/src/ui/img_ids.rs +++ b/voxygen/src/ui/img_ids.rs @@ -24,7 +24,7 @@ impl<'a> GraphicCreator<'a> for ImageGraphic { type Specifier = &'a str; fn new_graphic(specifier: Self::Specifier) -> Result { - Ok(Graphic::Image(load::(specifier)?)) + Ok(Graphic::Image(load::(specifier)?, None)) } } diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index d6eb8ae472..92743c625a 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -35,20 +35,20 @@ use cache::Cache; use common::{assets, util::srgba_to_linear}; use conrod_core::{ event::Input, - graph::Graph, + graph::{self, Graph}, image::{self, Map}, input::{touch::Touch, Motion, Widget}, render::{Primitive, PrimitiveKind}, text::{self, font}, widget::{self, id::Generator}, - Rect, UiBuilder, UiCell, + Rect, Scalar, UiBuilder, UiCell, }; +use core::{convert::TryInto, f32, f64, ops::Range}; use graphic::{Rotation, TexId}; +use hashbrown::hash_map::Entry; use std::{ - f32, f64, fs::File, io::{BufReader, Read}, - ops::Range, sync::Arc, time::Duration, }; @@ -66,7 +66,7 @@ enum DrawKind { Plain, } enum DrawCommand { - Draw { kind: DrawKind, verts: Range }, + Draw { kind: DrawKind, verts: Range }, Scissor(Aabr), WorldPos(Option), } @@ -74,14 +74,28 @@ impl DrawCommand { fn image(verts: Range, id: TexId) -> DrawCommand { DrawCommand::Draw { kind: DrawKind::Image(id), - verts, + verts: verts + .start + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!") + ..verts + .end + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!"), } } fn plain(verts: Range) -> DrawCommand { DrawCommand::Draw { kind: DrawKind::Plain, - verts, + verts: verts + .start + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!") + ..verts + .end + .try_into() + .expect("Vertex count for UI rendering does not fit in a u32!"), } } } @@ -103,6 +117,9 @@ pub struct Ui { cache: Cache, // Draw commands for the next render draw_commands: Vec, + // Mesh buffer for UI vertics; we reuse its allocation in order to limit vector reallocations + // during redrawing. + mesh: Mesh, // Model for drawing the ui model: DynamicModel, // Consts for default ui drawing position (ie the interface) @@ -128,6 +145,10 @@ impl Ui { let renderer = window.renderer_mut(); let mut ui = UiBuilder::new(win_dims).build(); + // NOTE: Since we redraw the actual frame each time whether or not the UI needs + // to be updated, there's no reason to set the redraw count higher than + // 1. + ui.set_num_redraw_frames(1); let tooltip_manager = TooltipManager::new( ui.widget_id_generator(), Duration::from_millis(1), @@ -140,6 +161,7 @@ impl Ui { image_map: Map::new(), cache: Cache::new(renderer)?, draw_commands: Vec::new(), + mesh: Mesh::new(), model: renderer.create_dynamic_model(100)?, interface_locals: renderer.create_consts(&[UiLocals::default()])?, default_globals: renderer.create_consts(&[Globals::default()])?, @@ -274,23 +296,55 @@ impl Ui { self.tooltip_manager .maintain(self.ui.global_input(), self.scale.scale_factor_logical()); - // Regenerate draw commands and associated models only if the ui changed - let mut primitives = match self.ui.draw_if_changed() { - Some(primitives) => primitives, - None => return, - }; + // Handle window resizing. + if let Some(new_dims) = self.window_resized.take() { + let (old_w, old_h) = self.scale.scaled_window_size().into_tuple(); + self.scale.window_resized(new_dims, renderer); + let (w, h) = self.scale.scaled_window_size().into_tuple(); + self.ui.handle_event(Input::Resize(w, h)); + + // Avoid panic in graphic cache when minimizing. + // Avoid resetting cache if window size didn't change + // Somewhat inefficient for elements that won't change size after a window + // resize + let res = renderer.get_resolution(); + self.need_cache_resize = res.x > 0 && res.y > 0 && !(old_w == w && old_h == h); + } if self.need_cache_resize { // Resize graphic cache - self.cache.resize_graphic_cache(renderer); - // Resize glyph cache - self.cache.resize_glyph_cache(renderer).unwrap(); + // FIXME: Handle errors here. + self.cache.resize(renderer).unwrap(); self.need_cache_resize = false; } - self.draw_commands.clear(); - let mut mesh = Mesh::new(); + let mut retry = false; + self.maintain_internal(renderer, view_projection_mat, &mut retry); + if retry { + // Update the glyph cache and try again. + self.maintain_internal(renderer, view_projection_mat, &mut retry); + } + } + + fn maintain_internal( + &mut self, + renderer: &mut Renderer, + view_projection_mat: Option>, + retry: &mut bool, + ) { + let (graphic_cache, text_cache, glyph_cache, cache_tex) = self.cache.cache_mut_and_tex(); + + let mut primitives = if *retry { + // If this is a retry, always redraw. + self.ui.draw() + } else { + // Otherwise, redraw only if widgets were actually updated. + match self.ui.draw_if_changed() { + Some(primitives) => primitives, + None => return, + } + }; let (half_res, x_align, y_align) = { let res = renderer.get_resolution(); @@ -301,6 +355,215 @@ impl Ui { ) }; + let ui = &self.ui; + let p_scale_factor = self.scale.scale_factor_physical(); + // Functions for converting for conrod scalar coords to GL vertex coords (-1.0 + // to 1.0). + let (ui_win_w, ui_win_h) = (ui.win_w, ui.win_h); + let vx = |x: f64| (x / ui_win_w * 2.0) as f32; + let vy = |y: f64| (y / ui_win_h * 2.0) as f32; + let gl_aabr = |rect: Rect| { + let (l, r, b, t) = rect.l_r_b_t(); + let min = Vec2::new( + ((vx(l) * half_res.x + x_align).round() - x_align) / half_res.x, + ((vy(b) * half_res.y + y_align).round() - y_align) / half_res.y, + ); + let max = Vec2::new( + ((vx(r) * half_res.x + x_align).round() - x_align) / half_res.x, + ((vy(t) * half_res.y + y_align).round() - y_align) / half_res.y, + ); + Aabr { min, max } + }; + + // let window_dim = ui.window_dim(); + let theme = &ui.theme; + let widget_graph = ui.widget_graph(); + let fonts = &ui.fonts; + let dpi_factor = p_scale_factor as f32; + + // We can use information about whether a widget was actually updated to more + // easily track cache invalidations. + let updated_widgets = ui.updated_widgets(); + let mut glyph_missing = false; + + updated_widgets.iter() + // Filter out widgets that are either: + // - not text primitives, or + // - are text primitives, but were both already in the cache, and not updated this + // frame. + // + // The reason the second condition is so complicated is that we want to handle cases + // where we cleared the whole cache, which can result in glyphs from text updated in a + // previous frame not being present in the text cache. + .filter_map(|(&widget_id, updated)| { + widget_graph.widget(widget_id) + .and_then(|widget| Some((widget.rect, widget.unique_widget_state::()?))) + .and_then(|(rect, text)| { + // NOTE: This fallback is weird and probably shouldn't exist. + let font_id = text.style.font_id(theme)/*.or_else(|| fonts.ids().next())*/?; + let font = fonts.get(font_id)?; + + Some((widget_id, updated, rect, text, font_id, font)) + }) + }) + // Recache the entry. + .for_each(|(widget_id, updated, rect, graph::UniqueWidgetState { state, style }, font_id, font)| { + let entry = match text_cache.entry(widget_id) { + Entry::Occupied(_) if !updated => return, + entry => entry, + }; + + // Retrieve styling. + let color = style.color(theme); + let font_size = style.font_size(theme); + let line_spacing = style.line_spacing(theme); + let justify = style.justify(theme); + let y_align = conrod_core::position::Align::End; + + let text = &state.string; + let line_infos = &state.line_infos; + + // Convert conrod coordinates to pixel coordinates. + let trans_x = |x: Scalar| (x + ui_win_w / 2.0) * dpi_factor as Scalar; + let trans_y = |y: Scalar| ((-y) + ui_win_h / 2.0) * dpi_factor as Scalar; + + // Produce the text layout iterators. + let lines = line_infos.iter().map(|info| &text[info.byte_range()]); + let line_rects = text::line::rects(line_infos.iter(), font_size, rect, + justify, y_align, line_spacing); + + // Grab the positioned glyphs from the text primitives. + let scale = text::f32_pt_to_scale(font_size as f32 * dpi_factor); + let positioned_glyphs = lines.zip(line_rects).flat_map(|(line, line_rect)| { + let (x, y) = (trans_x(line_rect.left()) as f32, trans_y(line_rect.bottom()) as f32); + let point = text::rt::Point { x, y }; + font.layout(line, scale, point) + }); + + // Reuse the mesh allocation if possible at this entry if possible; we + // then clear it to avoid using stale entries. + let mesh = entry.or_insert_with(Mesh::new); + mesh.clear(); + + let color = srgba_to_linear(color.to_fsa().into()); + + positioned_glyphs.for_each(|g| { + match glyph_cache.rect_for(font_id.index(), &g) { + Ok(Some((uv_rect, screen_rect))) => { + let uv = Aabr { + min: Vec2::new(uv_rect.min.x, uv_rect.max.y), + max: Vec2::new(uv_rect.max.x, uv_rect.min.y), + }; + let rect = Aabr { + min: Vec2::new( + vx(screen_rect.min.x as f64 / p_scale_factor + - ui.win_w / 2.0), + vy(ui.win_h / 2.0 + - screen_rect.max.y as f64 / p_scale_factor), + ), + max: Vec2::new( + vx(screen_rect.max.x as f64 / p_scale_factor + - ui.win_w / 2.0), + vy(ui.win_h / 2.0 + - screen_rect.min.y as f64 / p_scale_factor), + ), + }; + mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text)); + }, + // Glyph not found, no-op. + Ok(None) => {}, + // Glyph was found, but was not yet cached; we'll need to add it to the + // cache and retry. + Err(_) => { + // Queue the unknown glyph to be cached. + glyph_missing = true; + } + } + + // NOTE: Important to do this for *all* glyphs to try to make sure that + // any that are uncached are part of the graph. Because we always + // clear the entire cache whenever a new glyph is encountered, by + // adding and checking all glyphs as they come in we guarantee that (1) + // any glyphs in the text cache are in the queue, and (2) any glyphs + // not in the text cache are either in the glyph cache, or (during our + // processing here) we set the retry flag to force a glyph cache + // update. Setting the flag causes all glyphs in the current queue to + // become part of the glyph cache during the second call to + // `maintain_internal`, so as long as the cache refresh succeeded, + // during the second call all glyphs will hit this branch as desired. + glyph_cache.queue_glyph(font_id.index(), g); + }); + }); + + if glyph_missing { + if *retry { + // If a glyph was missing and this was our second try, we know something was + // messed up during the glyph_cache redraw. It is possible that + // the queue contained unneeded glyphs, so we don't necessarily + // have to give up; a more precise enumeration of the + // glyphs required to render this frame might work out. However, this is a + // pretty remote edge case, so we opt to not care about this + // frame (we skip rendering it, basically), and just clear the + // text cache and glyph queue; next frame will then + // start out with an empty slate, and therefore will enqueue precisely the + // glyphs needed for that frame. If *that* frame fails, we're + // in bigger trouble. + text_cache.clear(); + glyph_cache.clear(); + glyph_cache.clear_queue(); + self.ui.needs_redraw(); + warn!("Could not recache queued glyphs, skipping frame."); + } else { + // NOTE: If this is the first round after encountering a new glyph, we just + // refresh the whole glyph cache. Technically this is not necessary since + // positioned_glyphs should still be accurate, but it's just the easiest way + // to ensure that all glyph positions get updated. It also helps keep the glyph + // cache reasonable by making sure any glyphs that subsequently get rendered are + // actually in the cache, including glyphs that were mapped to ids but didn't + // happen to be rendered on the frame where the cache was + // refreshed. + text_cache.clear(); + tracing::debug!("Updating glyphs and clearing text cache."); + + if let Err(err) = glyph_cache.cache_queued(|rect, data| { + let offset = [rect.min.x as u16, rect.min.y as u16]; + let size = [rect.width() as u16, rect.height() as u16]; + + let new_data = data + .iter() + .map(|x| [255, 255, 255, *x]) + .collect::>(); + + if let Err(err) = renderer.update_texture(cache_tex, offset, size, &new_data) { + warn!("Failed to update texture: {:?}", err); + } + }) { + // FIXME: If we actually hit this error, it's still possible we could salvage + // things in various ways (for instance, the current queue might have extra + // stuff in it, so we could try calling maintain_internal a + // third time with a fully clean queue; or we could try to + // increase the glyph texture size, etc. But hopefully + // we will not actually encounter this error. + warn!("Failed to cache queued glyphs: {:?}", err); + + // Clear queued glyphs, so that (hopefully) next time we won't have the + // offending glyph or glyph set. We then exit the loop and don't try to + // rerender the frame. + glyph_cache.clear_queue(); + self.ui.needs_redraw(); + } else { + // Successfully cached, so repeat the loop. + *retry = true; + } + } + + return; + } + + self.draw_commands.clear(); + let mesh = &mut self.mesh; + mesh.clear(); + enum State { Image(TexId), Plain, @@ -321,7 +584,6 @@ impl Ui { }; let mut placement = Placement::Interface; - let p_scale_factor = self.scale.scale_factor_physical(); // Switches to the `Plain` state and completes the previous `Command` if not // already in the `Plain` state. @@ -341,9 +603,8 @@ impl Ui { kind, scizzor, rect, - .. + id: widget_id, } = prim; - // Check for a change in the scissor. let new_scissor = { let (l, b, w, h) = scizzor.l_b_w_h(); @@ -351,8 +612,8 @@ impl Ui { // Calculate minimum x and y coordinates while // flipping y axis (from +up to +down) and // moving origin to top-left corner (from middle). - let min_x = self.ui.win_w / 2.0 + l; - let min_y = self.ui.win_h / 2.0 - b - h; + let min_x = ui.win_w / 2.0 + l; + let min_y = ui.win_h / 2.0 - b - h; let intersection = Aabr { min: Vec2 { x: (min_x * scale_factor) as u16, @@ -415,24 +676,6 @@ impl Ui { Placement::Interface => {}, } - // Functions for converting for conrod scalar coords to GL vertex coords (-1.0 - // to 1.0). - let (ui_win_w, ui_win_h) = (self.ui.win_w, self.ui.win_h); - let vx = |x: f64| (x / ui_win_w * 2.0) as f32; - let vy = |y: f64| (y / ui_win_h * 2.0) as f32; - let gl_aabr = |rect: Rect| { - let (l, r, b, t) = rect.l_r_b_t(); - let min = Vec2::new( - ((vx(l) * half_res.x + x_align).round() - x_align) / half_res.x, - ((vy(b) * half_res.y + y_align).round() - y_align) / half_res.y, - ); - let max = Vec2::new( - ((vx(r) * half_res.x + x_align).round() - x_align) / half_res.x, - ((vy(t) * half_res.y + y_align).round() - y_align) / half_res.y, - ); - Aabr { min, max } - }; - match kind { PrimitiveKind::Image { image_id, @@ -443,7 +686,6 @@ impl Ui { .image_map .get(&image_id) .expect("Image does not exist in image map"); - let graphic_cache = self.cache.graphic_cache_mut(); let gl_aabr = gl_aabr(rect); let (source_aabr, gl_size) = { // Transform the source rectangle into uv coordinate. @@ -451,7 +693,7 @@ impl Ui { let ((uv_l, uv_r, uv_b, uv_t), gl_size) = match graphic_cache.get_graphic(*graphic_id) { Some(Graphic::Blank) | None => continue, - Some(Graphic::Image(image)) => { + Some(Graphic::Image(image, ..)) => { source_rect.and_then(|src_rect| { let (image_w, image_h) = image.dimensions(); let (source_w, source_h) = src_rect.w_h(); @@ -573,67 +815,16 @@ impl Ui { Rotation::TargetNorth => UiMode::ImageTargetNorth, })); }, - PrimitiveKind::Text { - color, - text, - font_id, - } => { + PrimitiveKind::Text { .. } => { switch_to_plain_state!(); - let positioned_glyphs = text.positioned_glyphs(p_scale_factor as f32); - let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex(); - // Queue the glyphs to be cached. - for glyph in positioned_glyphs { - glyph_cache.queue_glyph(font_id.index(), glyph.clone()); - } - - if let Err(err) = glyph_cache.cache_queued(|rect, data| { - let offset = [rect.min.x as u16, rect.min.y as u16]; - let size = [rect.width() as u16, rect.height() as u16]; - - let new_data = data - .iter() - .map(|x| [255, 255, 255, *x]) - .collect::>(); - - if let Err(err) = - renderer.update_texture(cache_tex, offset, size, &new_data) - { - warn!("Failed to update texture: {:?}", err); - } - }) { - warn!("Failed to cache queued glyphs: {:?}", err); - // Clear uncachable glyphs from the queue - glyph_cache.clear_queue(); - } - - let color = srgba_to_linear(color.to_fsa().into()); - - for g in positioned_glyphs { - if let Ok(Some((uv_rect, screen_rect))) = - glyph_cache.rect_for(font_id.index(), g) - { - let uv = Aabr { - min: Vec2::new(uv_rect.min.x, uv_rect.max.y), - max: Vec2::new(uv_rect.max.x, uv_rect.min.y), - }; - let rect = Aabr { - min: Vec2::new( - vx(screen_rect.min.x as f64 / p_scale_factor - - self.ui.win_w / 2.0), - vy(self.ui.win_h / 2.0 - - screen_rect.max.y as f64 / p_scale_factor), - ), - max: Vec2::new( - vx(screen_rect.max.x as f64 / p_scale_factor - - self.ui.win_w / 2.0), - vy(self.ui.win_h / 2.0 - - screen_rect.min.y as f64 / p_scale_factor), - ), - }; - mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text)); - } - } + // Mesh should already be cached. + mesh.push_mesh( + text_cache + .get(&widget_id) + .as_deref() + .unwrap_or(&Mesh::new()), + ); }, PrimitiveKind::Rectangle { color } => { let color = srgba_to_linear(color.to_fsa().into()); @@ -755,8 +946,8 @@ impl Ui { State::Image(id) => DrawCommand::image(start..mesh.vertices().len(), id), }); - // Draw glyph cache (use for debugging). - /*self.draw_commands + /* // Draw glyph cache (use for debugging). + self.draw_commands .push(DrawCommand::Scissor(default_scissor(renderer))); start = mesh.vertices().len(); mesh.push_quad(create_ui_quad( @@ -772,32 +963,17 @@ impl Ui { UiMode::Text, )); self.draw_commands - .push(DrawCommand::plain(start..mesh.vertices().len()));*/ + .push(DrawCommand::plain(start..mesh.vertices().len())); */ // Create a larger dynamic model if the mesh is larger than the current model // size. - if self.model.vbuf.len() < mesh.vertices().len() { + if self.model.vbuf.len() < self.mesh.vertices().len() { self.model = renderer - .create_dynamic_model(mesh.vertices().len() * 4 / 3) + .create_dynamic_model(self.mesh.vertices().len() * 4 / 3) .unwrap(); } // Update model with new mesh. - renderer.update_model(&self.model, &mesh, 0).unwrap(); - - // Handle window resizing. - if let Some(new_dims) = self.window_resized.take() { - let (old_w, old_h) = self.scale.scaled_window_size().into_tuple(); - self.scale.window_resized(new_dims, renderer); - let (w, h) = self.scale.scaled_window_size().into_tuple(); - self.ui.handle_event(Input::Resize(w, h)); - - // Avoid panic in graphic cache when minimizing. - // Avoid resetting cache if window size didn't change - // Somewhat inefficient for elements that won't change size after a window - // resize - let res = renderer.get_resolution(); - self.need_cache_resize = res.x > 0 && res.y > 0 && !(old_w == w && old_h == h); - } + renderer.update_model(&self.model, &self.mesh, 0).unwrap(); } pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts>) { @@ -818,7 +994,7 @@ impl Ui { DrawKind::Plain => self.cache.glyph_cache_tex(), }; let model = self.model.submodel(verts.clone()); - renderer.render_ui_element(&model, tex, scissor, globals, locals); + renderer.render_ui_element(model, tex, scissor, globals, locals); }, } } diff --git a/voxygen/src/ui/widgets/ingame.rs b/voxygen/src/ui/widgets/ingame.rs index e8f5ccf49f..957b049343 100644 --- a/voxygen/src/ui/widgets/ingame.rs +++ b/voxygen/src/ui/widgets/ingame.rs @@ -54,7 +54,7 @@ pub struct IngameParameters { } pub struct State { - id: Option, + id: widget::Id, pub parameters: IngameParameters, } @@ -78,7 +78,7 @@ impl Widget for Ingame { fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State { State { - id: Some(id_gen.next()), + id: id_gen.next(), parameters: IngameParameters { num: self.prim_num, pos: self.pos, @@ -112,7 +112,7 @@ impl Widget for Ingame { }); } - widget.set_ingame(state.id.unwrap(), ui) + widget.set_ingame(state.id, ui) } fn default_x_position(&self, _: &Ui) -> Position { Position::Absolute(0.0) } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 916281e36f..2a53eb5328 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -529,7 +529,7 @@ impl Window { let (window, device, factory, win_color_view, win_depth_view) = glutin::ContextBuilder::new() - .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) + .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 3))) .with_vsync(false) .with_gfx_color_depth::() .build_windowed(win_builder, &event_loop) @@ -585,9 +585,7 @@ impl Window { factory, win_color_view, win_depth_view, - settings.graphics.aa_mode, - settings.graphics.cloud_mode, - settings.graphics.fluid_mode, + settings.graphics.render_mode.clone(), )?, window, cursor_grabbed: false, diff --git a/world/Cargo.toml b/world/Cargo.toml index e2d3cb682e..dbee670446 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -9,9 +9,9 @@ bincode = "1.2.0" common = { package = "veloren-common", path = "../common" } bitvec = "0.17.4" fxhash = "0.2.1" -image = { version = "0.22.5", default-features = false, features = ["png"] } +image = { version = "0.23.8", default-features = false, features = ["png"] } itertools = "0.9" -vek = "0.11.0" +vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } noise = { version = "0.6.0", default-features = false } num = "0.2" ordered-float = "1.0" @@ -23,10 +23,10 @@ rand_chacha = "0.2.1" arr_macro = "0.1.2" packed_simd = "0.3.3" rayon = "^1.3.0" -roots = "0.0.5" serde = { version = "1.0.110", features = ["derive"] } ron = { version = "0.6", default-features = false } [dev-dependencies] +criterion = "0.3" tracing-subscriber = { version = "0.2.3", default-features = false, features = ["fmt", "chrono", "ansi", "smallvec"] } minifb = "0.14.0" diff --git a/world/examples/settlement_viewer.rs b/world/examples/settlement_viewer.rs index 8c11222241..9813e7ca88 100644 --- a/world/examples/settlement_viewer.rs +++ b/world/examples/settlement_viewer.rs @@ -1,12 +1,15 @@ use rand::thread_rng; use vek::*; -use veloren_world::site::Settlement; +use veloren_world::{index::Index, site::Settlement, IndexRef}; const W: usize = 640; const H: usize = 480; #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 fn main() { + let seed = 1337; + let (ref index, ref colors) = Index::new(seed); + let mut win = minifb::Window::new("Settlement Viewer", W, H, minifb::WindowOptions::default()).unwrap(); @@ -14,6 +17,7 @@ fn main() { let mut focus = Vec2::::zero(); let mut zoom = 1.0; + let index = IndexRef { colors, index }; while win.is_open() { let mut buf = vec![0; W * H]; @@ -26,7 +30,7 @@ fn main() { let pos = focus + win_to_pos(Vec2::new(i, j)) * zoom; let color = settlement - .get_color(pos.map(|e| e.floor() as i32)) + .get_color(index, pos.map(|e| e.floor() as i32)) .unwrap_or(Rgb::new(35, 50, 20)); buf[j * W + i] = u32::from_le_bytes([color.b, color.g, color.r, 255]); diff --git a/world/examples/view.rs b/world/examples/view.rs index b137e8abbe..a8e5db830e 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -6,11 +6,13 @@ const W: usize = 640; const H: usize = 480; fn main() { - let world = World::generate(0, WorldOpts { + let (world, index) = World::generate(0, WorldOpts { seed_elements: true, ..WorldOpts::default() }); + let index = index.as_index_ref(); + let sampler = world.sample_columns(); let mut win = @@ -28,7 +30,7 @@ fn main() { let pos = focus + Vec2::new(i as i32, j as i32) * scale; let (alt, place) = sampler - .get((pos, world.index())) + .get((pos, index)) .map(|sample| { ( sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8, diff --git a/world/examples/water.rs b/world/examples/water.rs index 36303763c5..cba3bc7250 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -1,11 +1,19 @@ -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + terrain::{ + map::{MapConfig, MapDebug, MapSample}, + uniform_idx_as_vec2, vec2_as_uniform_idx, TerrainChunkSize, + }, + vol::RectVolSize, +}; +use rayon::prelude::*; use std::{f64, io::Write, path::PathBuf, time::SystemTime}; -use tracing::warn; -use tracing_subscriber; +use tracing::{warn, Level}; +use tracing_subscriber::{filter::LevelFilter, EnvFilter, FmtSubscriber}; use vek::*; use veloren_world::{ - sim::{self, MapConfig, MapDebug, WorldOpts, WORLD_SIZE}, - World, CONFIG, + sim::{self, get_horizon_map, sample_pos, sample_wpos, WorldOpts}, + util::Sampler, + ColumnSample, World, CONFIG, }; const W: usize = 1024; @@ -14,7 +22,10 @@ const H: usize = 1024; #[allow(clippy::needless_update)] // TODO: Pending review in #587 #[allow(clippy::unused_io_amount)] // TODO: Pending review in #587 fn main() { - tracing_subscriber::fmt::init(); + FmtSubscriber::builder() + .with_max_level(Level::ERROR) + .with_env_filter(EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into())) + .init(); // To load a map file of your choice, replace map_file with the name of your map // (stored locally in the map directory of your Veloren root), and swap the @@ -22,29 +33,110 @@ fn main() { let map_file = // "map_1575990726223.bin"; // "map_1575987666972.bin"; - "map_1585335358316.bin"; - let mut map_path = PathBuf::from("./maps"); - map_path.push(map_file); + // "map_1576046079066.bin"; + // "maps/071090_2x.bin"; + "map_1579539133272.bin"; + let mut _map_file = PathBuf::from("./maps"); + _map_file.push(map_file); - let world = World::generate(5284, WorldOpts { + let (world, index) = World::generate(5284, WorldOpts { seed_elements: false, - //world_file: sim::FileOpts::Load(map_path), - world_file: sim::FileOpts::Save, + world_file: sim::FileOpts::LoadAsset(veloren_world::sim::DEFAULT_WORLD_MAP.into()), + // world_file: sim::FileOpts::Load(_map_file), + // world_file: sim::FileOpts::Save, ..WorldOpts::default() }); + let index = index.as_index_ref(); + tracing::info!("Sampling data..."); + let sampler = world.sim(); + let map_size_lg = sampler.map_size_lg(); + + let samples_data = { + let column_sample = world.sample_columns(); + (0..map_size_lg.chunks_len()) + .into_par_iter() + .map(|posi| { + column_sample.get(( + uniform_idx_as_vec2(map_size_lg, posi) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + index, + )) + }) + .collect::>() + .into_boxed_slice() + }; + let refresh_map_samples = |config: &MapConfig, samples: Option<&[Option]>| { + (0..map_size_lg.chunks_len()) + .into_par_iter() + .map(|posi| { + sample_pos( + config, + sampler, + index, + samples, + uniform_idx_as_vec2(map_size_lg, posi), + ) + }) + .collect::>() + .into_boxed_slice() + }; + let get_map_sample = |map_samples: &[MapSample], pos: Vec2| { + if pos.reduce_partial_min() >= 0 + && pos.x < map_size_lg.chunks().x as i32 + && pos.y < map_size_lg.chunks().y as i32 + { + map_samples[vec2_as_uniform_idx(map_size_lg, pos)].clone() + } else { + MapSample { + alt: 0.0, + rgb: Rgb::new(0, 0, 0), + connections: None, + downhill_wpos: (pos + 1) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + } + } + }; + + let refresh_horizons = |is_basement, is_water| { + get_horizon_map( + map_size_lg, + Aabr { + min: Vec2::zero(), + max: map_size_lg.chunks().map(|e| e as i32), + }, + CONFIG.sea_level, + CONFIG.sea_level + sampler.max_height, + |posi| { + let sample = sampler.get(uniform_idx_as_vec2(map_size_lg, posi)).unwrap(); + if is_basement { + sample.alt + } else { + sample.basement + } + .max(if is_water { + sample.water_alt + } else { + -f32::INFINITY + }) + }, + |a| a, + |h| h, + /* |[al, ar]| [al, ar], + * |[hl, hr]| [hl, hr], */ + ) + .ok() + }; let mut win = minifb::Window::new("World Viewer", W, H, minifb::WindowOptions::default()).unwrap(); - let sampler = world.sim(); - let mut focus = Vec3::new(0.0, 0.0, CONFIG.sea_level as f64); // Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain // makes smaller differences in altitude appear larger. - let mut gain = CONFIG.mountain_scale; + let mut gain = /*CONFIG.mountain_scale*/sampler.max_height; // The Z component during normal calculations is multiplied by gain; thus, - let mut lgain = 1.0; - let mut scale = WORLD_SIZE.x as f64 / W as f64; + let mut fov = 1.0; + let mut scale = + (map_size_lg.chunks().x as f64 / W as f64).max(map_size_lg.chunks().y as f64 / H as f64); // Right-handed coordinate system: light is going left, down, and "backwards" // (i.e. on the map, where we translate the y coordinate on the world map to @@ -54,7 +146,7 @@ fn main() { // // "In world space the x-axis will be pointing east, the y-axis up and the // z-axis will be pointing south" - let mut light_direction = Vec3::new(-0.8, -1.0, 0.3); + let mut light_direction = Vec3::new(-/*0.8*/1.3, -1.0, 0.3); let mut is_basement = false; let mut is_water = true; @@ -62,14 +154,21 @@ fn main() { let mut is_temperature = true; let mut is_humidity = true; + let mut horizons = refresh_horizons(is_basement, is_water); + let mut samples = None; + + let mut samples_changed = true; + let mut map_samples: Box<[_]> = Box::new([]); while win.is_open() { let config = MapConfig { + map_size_lg, dimensions: Vec2::new(W, H), focus, gain, - lgain, + fov, scale, light_direction, + horizons: horizons.as_ref(), /* .map(|(a, b)| (&**a, &**b)) */ is_basement, is_water, @@ -79,36 +178,51 @@ fn main() { is_debug: true, }; + if samples_changed { + map_samples = refresh_map_samples(&config, samples); + }; + let mut buf = vec![0; W * H]; let MapDebug { rivers, lakes, oceans, quads, - } = config.generate(sampler, world.index(), |pos, (r, g, b, a)| { - let i = pos.x; - let j = pos.y; - buf[j * W + i] = u32::from_le_bytes([b, g, r, a]); - }); + } = config.generate( + |pos| get_map_sample(&map_samples, pos), + |pos| sample_wpos(&config, sampler, pos), + |pos, (r, g, b, a)| { + let i = pos.x; + let j = pos.y; + buf[j * W + i] = u32::from_le_bytes([b, g, r, a]); + }, + ); if win.is_key_down(minifb::Key::F4) { + // Feedback is important since on large maps it can be hard to tell if the + // keypress registered or not. + println!("Taking screenshot..."); if let Some(len) = (W * H) .checked_mul(scale as usize) .and_then(|acc| acc.checked_mul(scale as usize)) { let x = (W as f64 * scale) as usize; let y = (H as f64 * scale) as usize; - let config = sim::MapConfig { + let config = MapConfig { dimensions: Vec2::new(x, y), scale: 1.0, ..config }; let mut buf = vec![0u8; 4 * len]; - config.generate(sampler, world.index(), |pos, (r, g, b, a)| { - let i = pos.x; - let j = pos.y; - (&mut buf[(j * x + i) * 4..]).write(&[r, g, b, a]).unwrap(); - }); + config.generate( + |pos| get_map_sample(&map_samples, pos), + |pos| sample_wpos(&config, sampler, pos), + |pos, (r, g, b, a)| { + let i = pos.x; + let j = pos.y; + (&mut buf[(j * x + i) * 4..]).write(&[r, g, b, a]).unwrap(); + }, + ); // TODO: Justify fits in u32. let world_map = image::RgbaImage::from_raw(x as u32, y as u32, buf) .expect("Image dimensions must be valid"); @@ -140,7 +254,7 @@ fn main() { Land(adjacent): (X = temp, Y = humidity): {:?}\nRivers: {:?}\nLakes: \ {:?}\nOceans: {:?}\nTotal water: {:?}\nTotal land(adjacent): {:?}", gain, - lgain, + fov, scale, focus, light_direction, @@ -178,18 +292,39 @@ fn main() { let is_camera = win.is_key_down(minifb::Key::C); if win.is_key_down(minifb::Key::B) { is_basement ^= true; + samples_changed = true; + horizons = horizons.and_then(|_| refresh_horizons(is_basement, is_water)); } if win.is_key_down(minifb::Key::H) { is_humidity ^= true; + samples_changed = true; } if win.is_key_down(minifb::Key::T) { is_temperature ^= true; + samples_changed = true; } if win.is_key_down(minifb::Key::O) { is_water ^= true; + samples_changed = true; + horizons = horizons.and_then(|_| refresh_horizons(is_basement, is_water)); } if win.is_key_down(minifb::Key::L) { - is_shaded ^= true; + if is_camera { + // TODO: implement removing horizon mapping. + horizons = if horizons.is_some() { + None + } else { + refresh_horizons(is_basement, is_water) + }; + samples_changed = true; + } else { + is_shaded ^= true; + samples_changed = true; + } + } + if win.is_key_down(minifb::Key::M) { + samples = samples.xor(Some(&*samples_data)); + samples_changed = true; } if win.is_key_down(minifb::Key::W) { if is_camera { @@ -221,8 +356,8 @@ fn main() { } if win.is_key_down(minifb::Key::Q) { if is_camera { - if (lgain * 2.0).is_normal() { - lgain *= 2.0; + if (fov * 2.0).is_normal() { + fov *= 2.0; } } else { gain += 64.0; @@ -230,8 +365,8 @@ fn main() { } if win.is_key_down(minifb::Key::E) { if is_camera { - if (lgain / 2.0).is_normal() { - lgain /= 2.0; + if (fov / 2.0).is_normal() { + fov /= 2.0; } } else { gain = (gain - 64.0).max(64.0); @@ -240,6 +375,7 @@ fn main() { if win.is_key_down(minifb::Key::R) { if is_camera { focus.z += spd * scale; + samples_changed = true; } else { if (scale * 2.0).is_normal() { scale *= 2.0; @@ -249,6 +385,7 @@ fn main() { if win.is_key_down(minifb::Key::F) { if is_camera { focus.z -= spd * scale; + samples_changed = true; } else { if (scale / 2.0).is_normal() { scale /= 2.0; diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 77f7d3ae08..6d34ccc466 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -3,15 +3,28 @@ mod natural; use crate::{ column::{ColumnGen, ColumnSample}, util::{RandomField, Sampler, SmallCache}, - Index, + IndexRef, }; use common::{ - terrain::{structure::StructureBlock, Block, BlockKind, Structure}, + terrain::{ + structure::{self, StructureBlock}, + Block, BlockKind, Structure, + }, vol::{ReadVol, Vox}, }; -use std::ops::{Div, Mul}; +use core::ops::{Div, Mul, Range}; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub pyramid: (u8, u8, u8), + // TODO(@Sharp): After the merge, construct enough infrastructure to make it convenient to + // define mapping functions over the input; i.e. we should be able to interpret some fields as + // defining App, Arg>, where Fun : (Context, Arg) → (S, Type). + pub structure_blocks: structure::structure_block::PureCases>>, +} + pub struct BlockGen<'a> { pub column_cache: SmallCache>>, pub column_gen: ColumnGen<'a>, @@ -29,21 +42,21 @@ impl<'a> BlockGen<'a> { column_gen: &ColumnGen<'a>, cache: &'b mut SmallCache>>, wpos: Vec2, - index: &Index, + index: IndexRef<'a>, ) -> Option<&'b ColumnSample<'a>> { cache .get(wpos, |wpos| column_gen.get((wpos, index))) .as_ref() } - fn get_cliff_height( + pub fn get_cliff_height( column_gen: &ColumnGen<'a>, cache: &mut SmallCache>>, wpos: Vec2, close_cliffs: &[(Vec2, u32); 9], cliff_hill: f32, tolerance: f32, - index: &Index, + index: IndexRef<'a>, ) -> f32 { close_cliffs.iter().fold( 0.0f32, @@ -89,7 +102,7 @@ impl<'a> BlockGen<'a> { ) } - pub fn get_z_cache(&mut self, wpos: Vec2, index: &'a Index) -> Option> { + pub fn get_z_cache(&mut self, wpos: Vec2, index: IndexRef<'a>) -> Option> { let BlockGen { column_cache, column_gen, @@ -143,7 +156,7 @@ impl<'a> BlockGen<'a> { wpos: Vec3, z_cache: Option<&ZCache>, only_structures: bool, - index: &Index, + index: IndexRef<'a>, ) -> Option { let BlockGen { column_cache, @@ -237,18 +250,7 @@ impl<'a> BlockGen<'a> { // Sample blocks - // let stone_col = Rgb::new(195, 187, 201); - - // let dirt_col = Rgb::new(79, 67, 60); - - let _air = Block::empty(); - // let stone = Block::new(2, stone_col); - // let surface_stone = Block::new(1, Rgb::new(200, 220, 255)); - // let dirt = Block::new(1, dirt_col); - // let sand = Block::new(1, Rgb::new(180, 150, 50)); - // let warm_stone = Block::new(1, Rgb::new(165, 165, 130)); - - let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190)); + let water = Block::new(BlockKind::Water, Rgb::zero()); let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height); let block = if (wposf.z as f32) < height - grass_depth { @@ -389,7 +391,7 @@ impl<'a> BlockGen<'a> { .iter() .find_map(|st| { let (st, st_sample) = st.as_ref()?; - st.get(wpos, st_sample) + st.get(index, wpos, st_sample) }) .or(block); @@ -404,7 +406,11 @@ pub struct ZCache<'a> { } impl<'a> ZCache<'a> { - pub fn get_z_limits(&self, block_gen: &mut BlockGen, index: &Index) -> (f32, f32, f32) { + pub fn get_z_limits<'b>( + &self, + block_gen: &mut BlockGen<'b>, + index: IndexRef<'b>, + ) -> (f32, f32, f32) { let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0); let min = min - 4.0; @@ -492,7 +498,7 @@ impl StructureInfo { } } - fn get(&self, wpos: Vec3, sample: &ColumnSample) -> Option { + fn get(&self, index: IndexRef, wpos: Vec3, sample: &ColumnSample) -> Option { match self.meta { StructureMeta::Pyramid { height } => { if wpos.z - self.pos.z @@ -501,7 +507,10 @@ impl StructureInfo { .map(|e: i32| (e.abs() / 2) * 2) .reduce_max() { - Some(Block::new(BlockKind::Dense, Rgb::new(203, 170, 146))) + Some(Block::new( + BlockKind::Dense, + index.colors.block.pyramid.into(), + )) } else { None } @@ -517,6 +526,7 @@ impl StructureInfo { .ok() .and_then(|b| { block_from_structure( + index, *b, block_pos, self.pos.into(), @@ -530,6 +540,7 @@ impl StructureInfo { } pub fn block_from_structure( + index: IndexRef, sblock: StructureBlock, pos: Vec3, structure_pos: Vec2, @@ -543,85 +554,58 @@ pub fn block_from_structure( match sblock { StructureBlock::None => None, + StructureBlock::Hollow => Some(Block::empty()), StructureBlock::Grass => Some(Block::new( BlockKind::Normal, sample.surface_color.map(|e| (e * 255.0) as u8), )), - StructureBlock::TemperateLeaves => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(0.0, 132.0, 94.0), - Rgb::new(142.0, 181.0, 0.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::PineLeaves => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp) - .map(|e| e as u8), - )), - StructureBlock::PalmLeavesInner => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(61.0, 166.0, 43.0), - Rgb::new(29.0, 130.0, 32.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::PalmLeavesOuter => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(62.0, 171.0, 38.0), - Rgb::new(45.0, 171.0, 65.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::new(100, 150, 255))), - StructureBlock::GreenSludge => Some(Block::new(BlockKind::Water, Rgb::new(30, 126, 23))), - StructureBlock::Acacia => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp( - Rgb::new(15.0, 126.0, 50.0), - Rgb::new(30.0, 180.0, 10.0), - lerp, - ) - .map(|e| e as u8), + StructureBlock::Normal(color) => { + Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty()) + }, + // Water / sludge throw away their color bits currently, so we don't set anyway. + StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::zero())), + StructureBlock::GreenSludge => Some(Block::new( + BlockKind::Water, + // TODO: If/when liquid supports other colors again, revisit this. + Rgb::zero(), )), + // None of these BlockKinds has an orientation, so we just use zero for the other color + // bits. StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 3 > 0 { Block::empty() } else { - Block::new(BlockKind::Apple, Rgb::new(1, 1, 1)) + Block::new(BlockKind::Apple, Rgb::zero()) }), StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 { Block::empty() } else { - Block::new(BlockKind::Coconut, Rgb::new(1, 1, 1)) + Block::new(BlockKind::Coconut, Rgb::zero()) }), StructureBlock::Chest => Some(if structure_seed % 10 < 7 { Block::empty() } else { - Block::new(BlockKind::Chest, Rgb::new(1, 1, 1)) + Block::new(BlockKind::Chest, Rgb::zero()) }), - StructureBlock::Liana => Some(Block::new( - BlockKind::Liana, - Lerp::lerp( - Rgb::new(0.0, 125.0, 107.0), - Rgb::new(0.0, 155.0, 129.0), - lerp, - ) - .map(|e| e as u8), - )), - StructureBlock::Mangrove => Some(Block::new( - BlockKind::Leaves, - Lerp::lerp(Rgb::new(32.0, 56.0, 22.0), Rgb::new(57.0, 69.0, 27.0), lerp) - .map(|e| e as u8), - )), - StructureBlock::Hollow => Some(Block::empty()), - StructureBlock::Normal(color) => { - Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty()) - }, + // We interpolate all these BlockKinds as needed. + StructureBlock::TemperateLeaves + | StructureBlock::PineLeaves + | StructureBlock::PalmLeavesInner + | StructureBlock::PalmLeavesOuter + | StructureBlock::Acacia + | StructureBlock::Liana + | StructureBlock::Mangrove => sblock + .elim_case_pure(&index.colors.block.structure_blocks) + .as_ref() + .map(|range| { + Block::new( + BlockKind::Leaves, + Rgb::::lerp( + Rgb::::from(range.start).map(f32::from), + Rgb::::from(range.end).map(f32::from), + lerp, + ) + .map(|e| e as u8), + ) + }), } } diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index e09ca2158a..8ccff506b0 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -3,7 +3,7 @@ use crate::{ all::ForestKind, column::{ColumnGen, ColumnSample}, util::{RandomPerm, Sampler, SmallCache, UnitChooser}, - Index, CONFIG, + IndexRef, CONFIG, }; use common::terrain::Structure; use lazy_static::lazy_static; @@ -20,7 +20,7 @@ pub fn structure_gen<'a>( st_pos: Vec2, st_seed: u32, st_sample: &ColumnSample, - index: &'a Index, + index: IndexRef<'a>, ) -> Option { // Assuming it's a tree... figure out when it SHOULDN'T spawn let random_seed = (st_seed as f64) / (u32::MAX as f64); diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 9d9058b925..d49f610d4f 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -4,6 +4,7 @@ mod econ; use self::{Occupation::*, Stock::*}; use crate::{ + config::CONFIG, sim::WorldSim, site::{Castle, Dungeon, Settlement, Site as WorldSite}, util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS}, @@ -14,7 +15,7 @@ use common::{ path::Path, spiral::Spiral2d, store::{Id, Store}, - terrain::TerrainChunkSize, + terrain::{MapSizeLg, TerrainChunkSize}, vol::RectVolSize, }; use core::{ @@ -29,7 +30,13 @@ use rand_chacha::ChaChaRng; use tracing::{debug, info, warn}; use vek::*; -const INITIAL_CIV_COUNT: usize = (crate::sim::WORLD_SIZE.x * crate::sim::WORLD_SIZE.y * 3) / 65536; //48 at default scale +const fn initial_civ_count(map_size_lg: MapSizeLg) -> u32 { + // NOTE: since map_size_lg's dimensions must fit in a u16, we can safely add + // them here. + // + // NOTE: 48 at "default" scale of 10 × 10 chunk bits (1024 × 1024 chunks). + (3 << (map_size_lg.vec().x + map_size_lg.vec().y)) >> 16 +} #[allow(clippy::type_complexity)] // TODO: Pending review in #587 #[derive(Default)] @@ -74,21 +81,23 @@ impl Civs { pub fn generate(seed: u32, sim: &mut WorldSim, index: &mut Index) -> Self { let mut this = Self::default(); let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + let initial_civ_count = initial_civ_count(sim.map_size_lg()); let mut ctx = GenCtx { sim, rng }; + // TODO: Care about world size when generating caves. for _ in 0..100 { this.generate_cave(&mut ctx); } - for _ in 0..INITIAL_CIV_COUNT { + for _ in 0..initial_civ_count { debug!("Creating civilisation..."); if this.birth_civ(&mut ctx.reseed()).is_none() { warn!("Failed to find starting site for civilisation."); } } - info!(?INITIAL_CIV_COUNT, "all civilisations created"); + info!(?initial_civ_count, "all civilisations created"); - for _ in 0..INITIAL_CIV_COUNT * 3 { + for _ in 0..initial_civ_count * 3 { attempt(5, || { let (kind, size) = match ctx.rng.gen_range(0, 8) { 0 => (SiteKind::Castle, 3), @@ -163,6 +172,11 @@ impl Civs { .filter(|chunk| !chunk.river.near_water()) .map(|chunk| { let diff = Lerp::lerp_precise(chunk.alt, center_alt, factor) - chunk.alt; + // Make sure we don't fall below sea level (fortunately, we don't have + // to worry about the case where water_alt is already set to a correct + // value higher than alt, since this chunk should have been filtered + // out in that case). + chunk.water_alt = CONFIG.sea_level.max(chunk.water_alt + diff); chunk.alt += diff; chunk.basement += diff; chunk.rockiness = 0.0; diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 5f08f73f39..c8e07476e1 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,16 +1,19 @@ use crate::{ all::ForestKind, block::StructureMeta, - sim::{ - local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, Cave, Path, RiverKind, SimChunk, - WorldSim, - }, + sim::{local_cells, Cave, Path, RiverKind, SimChunk, WorldSim}, util::Sampler, - Index, CONFIG, + IndexRef, CONFIG, +}; +use common::{ + terrain::{ + quadratic_nearest_point, river_spline_coeffs, uniform_idx_as_vec2, vec2_as_uniform_idx, + TerrainChunkSize, + }, + vol::RectVolSize, }; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use noise::NoiseFn; -use roots::find_roots_cubic; +use serde::{Deserialize, Serialize}; use std::{ cmp::Reverse, f32, f64, @@ -23,6 +26,31 @@ pub struct ColumnGen<'a> { pub sim: &'a WorldSim, } +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub cold_grass: (f32, f32, f32), + pub warm_grass: (f32, f32, f32), + pub dark_grass: (f32, f32, f32), + pub wet_grass: (f32, f32, f32), + pub cold_stone: (f32, f32, f32), + pub hot_stone: (f32, f32, f32), + pub warm_stone: (f32, f32, f32), + pub beach_sand: (f32, f32, f32), + pub desert_sand: (f32, f32, f32), + pub snow: (f32, f32, f32), + + pub stone_col: (u8, u8, u8), + + pub dirt_low: (f32, f32, f32), + pub dirt_high: (f32, f32, f32), + + pub snow_high: (f32, f32, f32), + pub warm_stone_high: (f32, f32, f32), + + pub grass_high: (f32, f32, f32), + pub tropical_high: (f32, f32, f32), +} + impl<'a> ColumnGen<'a> { pub fn new(sim: &'a WorldSim) -> Self { Self { sim } } @@ -77,102 +105,8 @@ impl<'a> ColumnGen<'a> { } } -fn river_spline_coeffs( - // _sim: &WorldSim, - chunk_pos: Vec2, - spline_derivative: Vec2, - downhill_pos: Vec2, -) -> Vec3> { - let dxy = downhill_pos - chunk_pos; - // Since all splines have been precomputed, we don't have to do that much work - // to evaluate the spline. The spline is just ax^2 + bx + c = 0, where - // - // a = dxy - chunk.river.spline_derivative - // b = chunk.river.spline_derivative - // c = chunk_pos - let spline_derivative = spline_derivative.map(|e| e as f64); - Vec3::new(dxy - spline_derivative, spline_derivative, chunk_pos) -} - -/// Find the nearest point from a quadratic spline to this point (in terms of t, -/// the "distance along the curve" by which our spline is parameterized). Note -/// that if t < 0.0 or t >= 1.0, we probably shouldn't be considered "on the -/// curve"... hopefully this works out okay and gives us what we want (a -/// river that extends outwards tangent to a quadratic curve, with width -/// configured by distance along the line). -#[allow(clippy::let_and_return)] // TODO: Pending review in #587 -#[allow(clippy::many_single_char_names)] -pub fn quadratic_nearest_point( - spline: &Vec3>, - point: Vec2, -) -> Option<(f64, Vec2, f64)> { - let a = spline.z.x; - let b = spline.y.x; - let c = spline.x.x; - let d = point.x; - let e = spline.z.y; - let f = spline.y.y; - let g = spline.x.y; - let h = point.y; - // This is equivalent to solving the following cubic equation (derivation is a - // bit annoying): - // - // A = 2(c^2 + g^2) - // B = 3(b * c + g * f) - // C = ((a - d) * 2 * c + b^2 + (e - h) * 2 * g + f^2) - // D = ((a - d) * b + (e - h) * f) - // - // Ax³ + Bx² + Cx + D = 0 - // - // Once solved, this yield up to three possible values for t (reflecting minimal - // and maximal values). We should choose the minimal such real value with t - // between 0.0 and 1.0. If we fall outside those bounds, then we are - // outside the spline and return None. - let a_ = (c * c + g * g) * 2.0; - let b_ = (b * c + g * f) * 3.0; - let a_d = a - d; - let e_h = e - h; - let c_ = a_d * c * 2.0 + b * b + e_h * g * 2.0 + f * f; - let d_ = a_d * b + e_h * f; - let roots = find_roots_cubic(a_, b_, c_, d_); - let roots = roots.as_ref(); - - let min_root = roots - .iter() - .copied() - .filter_map(|root| { - let river_point = spline.x * root * root + spline.y * root + spline.z; - let river_zero = spline.z; - let river_one = spline.x + spline.y + spline.z; - if root > 0.0 && root < 1.0 { - Some((root, river_point)) - } else if river_point.distance_squared(river_zero) < 0.5 { - Some((root, /*river_point*/ river_zero)) - } else if river_point.distance_squared(river_one) < 0.5 { - Some((root, /*river_point*/ river_one)) - } else { - None - } - }) - .map(|(root, river_point)| { - let river_distance = river_point.distance_squared(point); - (root, river_point, river_distance) - }) - // In the (unlikely?) case that distances are equal, prefer the earliest point along the - // river. - .min_by(|&(ap, _, a), &(bp, _, b)| { - (a, ap < 0.0 || ap > 1.0, ap) - .partial_cmp(&(b, bp < 0.0 || bp > 1.0, bp)) - .unwrap() - }); - min_root -} - -impl<'a, 'b> Sampler<'b> for ColumnGen<'a> -where - 'a: 'b, -{ - type Index = (Vec2, &'b Index); +impl<'a> Sampler<'a> for ColumnGen<'a> { + type Index = (Vec2, IndexRef<'a>); type Sample = Option>; #[allow(clippy::float_cmp)] // TODO: Pending review in #587 @@ -202,12 +136,13 @@ where let chunk_warp_factor = sim.get_interpolated_monotone(wpos, |chunk| chunk.warp_factor)?; let sim_chunk = sim.get(chunk_pos)?; let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); - let my_chunk_idx = vec2_as_uniform_idx(chunk_pos); - let neighbor_river_data = local_cells(my_chunk_idx).filter_map(|neighbor_idx: usize| { - let neighbor_pos = uniform_idx_as_vec2(neighbor_idx); - let neighbor_chunk = sim.get(neighbor_pos)?; - Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river)) - }); + let my_chunk_idx = vec2_as_uniform_idx(self.sim.map_size_lg(), chunk_pos); + let neighbor_river_data = + local_cells(self.sim.map_size_lg(), my_chunk_idx).filter_map(|neighbor_idx: usize| { + let neighbor_pos = uniform_idx_as_vec2(self.sim.map_size_lg(), neighbor_idx); + let neighbor_chunk = sim.get(neighbor_pos)?; + Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river)) + }); let lake_width = (TerrainChunkSize::RECT_SIZE.x as f64 * (2.0f64.sqrt())) + 12.0; let neighbor_river_data = neighbor_river_data.map(|(posj, chunkj, river)| { let kind = match river.river_kind { @@ -856,25 +791,47 @@ where .add(marble_small.sub(0.5).mul(0.25)); // Colours - let cold_grass = Rgb::new(0.0, 0.5, 0.25); - let warm_grass = Rgb::new(0.4, 0.8, 0.0); - let dark_grass = Rgb::new(0.15, 0.4, 0.1); - let wet_grass = Rgb::new(0.1, 0.8, 0.2); - let cold_stone = Rgb::new(0.57, 0.67, 0.8); - let hot_stone = Rgb::new(0.07, 0.07, 0.06); - let warm_stone = Rgb::new(0.77, 0.77, 0.64); - let beach_sand = Rgb::new(0.8, 0.75, 0.5); - let desert_sand = Rgb::new(0.7, 0.4, 0.25); - let snow = Rgb::new(0.8, 0.85, 1.0); + let Colors { + cold_grass, + warm_grass, + dark_grass, + wet_grass, + cold_stone, + hot_stone, + warm_stone, + beach_sand, + desert_sand, + snow, + stone_col, + dirt_low, + dirt_high, + snow_high, + warm_stone_high, + grass_high, + tropical_high, + } = index.colors.column; - let stone_col = Rgb::new(195, 187, 201); - let dirt = Lerp::lerp( - Rgb::new(0.075, 0.07, 0.3), - Rgb::new(0.75, 0.55, 0.1), - marble, - ); - let tundra = Lerp::lerp(snow, Rgb::new(0.01, 0.3, 0.0), 0.4 + marble * 0.6); - let dead_tundra = Lerp::lerp(warm_stone, Rgb::new(0.3, 0.12, 0.2), marble); + let cold_grass = cold_grass.into(); + let warm_grass = warm_grass.into(); + let dark_grass = dark_grass.into(); + let wet_grass = wet_grass.into(); + let cold_stone = cold_stone.into(); + let hot_stone = hot_stone.into(); + let warm_stone: Rgb = warm_stone.into(); + let beach_sand = beach_sand.into(); + let desert_sand = desert_sand.into(); + let snow = snow.into(); + let stone_col = stone_col.into(); + let dirt_low: Rgb = dirt_low.into(); + let dirt_high = dirt_high.into(); + let snow_high = snow_high.into(); + let warm_stone_high = warm_stone_high.into(); + let grass_high = grass_high.into(); + let tropical_high = tropical_high.into(); + + let dirt = Lerp::lerp(dirt_low, dirt_high, marble); + let tundra = Lerp::lerp(snow, snow_high, 0.4 + marble * 0.6); + let dead_tundra = Lerp::lerp(warm_stone, warm_stone_high, marble); let cliff = Rgb::lerp(cold_stone, hot_stone, marble); let grass = Rgb::lerp( @@ -890,14 +847,14 @@ where let tropical = Rgb::lerp( Rgb::lerp( grass, - Rgb::new(0.15, 0.2, 0.15), + grass_high, marble_small .sub(0.5) .mul(0.2) .add(0.75.mul(1.0.sub(humidity))) .powf(0.667), ), - Rgb::new(0.87, 0.62, 0.56), + tropical_high, marble.powf(1.5).sub(0.5).mul(4.0), ); diff --git a/world/src/config.rs b/world/src/config.rs index dffd712f7d..5f8914d6e1 100644 --- a/world/src/config.rs +++ b/world/src/config.rs @@ -64,5 +64,5 @@ pub const CONFIG: Config = Config { river_roughness: 0.06125, river_max_width: 2.0, river_min_height: 0.25, - river_width_to_depth: 1.0, + river_width_to_depth: 8.0, }; diff --git a/world/src/index.rs b/world/src/index.rs index ed6a6ce157..d8dedd23ed 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -1,21 +1,104 @@ -use crate::site::Site; -use common::store::Store; +use crate::{site::Site, Colors}; +use common::{ + assets::{self, watch::ReloadIndicator}, + store::Store, +}; +use core::ops::Deref; use noise::{Seedable, SuperSimplex}; +use std::sync::Arc; + +const WORLD_COLORS_MANIFEST: &str = "world.style.colors"; pub struct Index { pub seed: u32, pub time: f32, pub noise: Noise, pub sites: Store, + indicator: ReloadIndicator, +} + +/// An owned reference to indexed data. +/// +/// The data are split out so that we can replace the colors without disturbing +/// the rest of the index, while also keeping all the data within a single +/// indirection. +#[derive(Clone)] +pub struct IndexOwned { + colors: Arc, + index: Arc, +} + +impl Deref for IndexOwned { + type Target = Index; + + fn deref(&self) -> &Self::Target { &self.index } +} + +/// A shared reference to indexed data. +/// +/// This is copyable and can be used from either style of index. +#[derive(Clone, Copy)] +pub struct IndexRef<'a> { + pub colors: &'a Colors, + pub index: &'a Index, +} + +impl<'a> Deref for IndexRef<'a> { + type Target = Index; + + fn deref(&self) -> &Self::Target { &self.index } } impl Index { - pub fn new(seed: u32) -> Self { + /// NOTE: Panics if the color manifest cannot be loaded. + pub fn new(seed: u32) -> (Self, Arc) { + let mut indicator = ReloadIndicator::new(); + let colors = assets::load_watched::(WORLD_COLORS_MANIFEST, &mut indicator) + .expect("Could not load world colors!"); + + ( + Self { + seed, + time: 0.0, + noise: Noise::new(seed), + sites: Store::default(), + indicator, + }, + colors, + ) + } +} + +impl IndexOwned { + pub fn new(index: Index, colors: Arc) -> Self { Self { - seed, - time: 0.0, - noise: Noise::new(seed), - sites: Store::default(), + index: Arc::new(index), + colors, + } + } + + /// NOTE: Callback is called only when colors actually have to be reloaded. + /// The server is responsible for making sure that all affected chunks are + /// reloaded; a naive approach will just regenerate every chunk on the + /// server, but it is possible that eventually we can find a better + /// solution. + /// + /// Ideally, this should be called about once per tick. + pub fn reload_colors_if_changed( + &mut self, + reload: impl FnOnce(&mut Self) -> R, + ) -> Option { + self.indicator.reloaded().then(move || { + // We know the asset was loaded before, so load_expect should be fine. + self.colors = assets::load_expect::(WORLD_COLORS_MANIFEST); + reload(self) + }) + } + + pub fn as_index_ref(&self) -> IndexRef { + IndexRef { + colors: &self.colors, + index: &self.index, } } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 72986bb1ec..d358e6286e 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -2,7 +2,7 @@ use crate::{ column::ColumnSample, sim::SimChunk, util::{RandomField, Sampler}, - Index, + IndexRef, }; use common::{ assets, comp, @@ -13,12 +13,19 @@ use common::{ }; use noise::NoiseFn; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use std::{ f32, ops::{Mul, Sub}, }; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub bridge: (u8, u8, u8), + pub stalagtite: (u8, u8, u8), +} + fn close(x: f32, tgt: f32, falloff: f32) -> f32 { (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5) } @@ -27,7 +34,7 @@ pub fn apply_scatter_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + index: IndexRef, chunk: &SimChunk, ) { use BlockKind::*; @@ -150,7 +157,7 @@ pub fn apply_paths_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - _index: &Index, + index: IndexRef, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -213,7 +220,10 @@ pub fn apply_paths_to<'a>( let _ = vol.set( Vec3::new(offs.x, offs.y, surface_z + z), if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 { - Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) + Block::new( + BlockKind::Normal, + noisy_color(index.colors.layer.bridge.into(), 8), + ) } else { let path_color = path.surface_color( col_sample.sub_surface_color.map(|e| (e * 255.0) as u8), @@ -238,7 +248,7 @@ pub fn apply_caves_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + index: IndexRef, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -297,7 +307,7 @@ pub fn apply_caves_to<'a>( for z in cave_roof - stalagtites..cave_roof { let _ = vol.set( Vec3::new(offs.x, offs.y, z), - Block::new(BlockKind::Rock, Rgb::broadcast(200)), + Block::new(BlockKind::Rock, index.colors.layer.stalagtite.into()), ); } @@ -325,7 +335,7 @@ pub fn apply_caves_supplement<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &(impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + index: IndexRef, supplement: &mut ChunkSupplement, ) { for y in 0..vol.size_xy().y as i32 { diff --git a/world/src/lib.rs b/world/src/lib.rs index d6ca1a0023..6a1a6e954d 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -1,7 +1,14 @@ #![deny(unsafe_code)] #![allow(incomplete_features)] #![allow(clippy::option_map_unit_fn)] -#![feature(arbitrary_enum_discriminant, const_generics, label_break_value)] +#![feature( + arbitrary_enum_discriminant, + bool_to_option, + const_generics, + const_panic, + label_break_value, + or_patterns +)] mod all; mod block; @@ -17,21 +24,26 @@ pub mod util; // Reexports pub use crate::config::CONFIG; +pub use block::BlockGen; +pub use column::ColumnSample; +pub use index::{IndexOwned, IndexRef}; use crate::{ - block::BlockGen, - column::{ColumnGen, ColumnSample}, + column::ColumnGen, index::Index, util::{Grid, Sampler}, }; use common::{ + assets::{self, Asset}, comp::{self, bird_medium, critter, quadruped_low, quadruped_medium, quadruped_small}, generation::{ChunkSupplement, EntityInfo}, + msg::server::WorldMapMsg, terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, vol::{ReadVol, RectVolSize, Vox, WriteVol}, }; use rand::Rng; -use std::time::Duration; +use serde::{Deserialize, Serialize}; +use std::{fs::File, io::BufReader, time::Duration}; use vek::*; #[derive(Debug)] @@ -42,35 +54,51 @@ pub enum Error { pub struct World { sim: sim::WorldSim, civs: civ::Civs, - index: Index, +} + +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub deep_stone_color: (u8, u8, u8), + pub block: block::Colors, + pub column: column::Colors, + pub layer: layer::Colors, + pub site: site::Colors, +} + +impl Asset for Colors { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) + } } impl World { - pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self { + pub fn generate(seed: u32, opts: sim::WorldOpts) -> (Self, IndexOwned) { + // NOTE: Generating index first in order to quickly fail if the color manifest + // is broken. + let (mut index, colors) = Index::new(seed); let mut sim = sim::WorldSim::generate(seed, opts); - let mut index = Index::new(seed); let civs = civ::Civs::generate(seed, &mut sim, &mut index); sim2::simulate(&mut index, &mut sim); - Self { sim, civs, index } + (Self { sim, civs }, IndexOwned::new(index, colors)) } pub fn sim(&self) -> &sim::WorldSim { &self.sim } pub fn civs(&self) -> &civ::Civs { &self.civs } - pub fn index(&self) -> &Index { &self.index } - pub fn tick(&self, _dt: Duration) { // TODO } - pub fn get_map_data(&self) -> Vec { self.sim.get_map(&self.index) } + pub fn get_map_data(&self, index: IndexRef) -> WorldMapMsg { self.sim.get_map(index) } pub fn sample_columns( &self, - ) -> impl Sampler, &Index), Sample = Option> + '_ { + ) -> impl Sampler, IndexRef), Sample = Option> + '_ { ColumnGen::new(&self.sim) } @@ -79,6 +107,7 @@ impl World { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn generate_chunk( &self, + index: IndexRef, chunk_pos: Vec2, // TODO: misleading name mut should_continue: impl FnMut() -> bool, @@ -89,7 +118,7 @@ impl World { let grid_border = 4; let zcache_grid = Grid::populate_from( TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, - |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, &self.index), + |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index), ); let air = Block::empty(); @@ -99,9 +128,9 @@ impl World { .get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2) .and_then(|zcache| zcache.as_ref()) .map(|zcache| zcache.sample.stone_col) - .unwrap_or(Rgb::new(125, 120, 130)), + .unwrap_or(index.colors.deep_stone_color.into()), ); - let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190)); + let water = Block::new(BlockKind::Water, Rgb::zero()); let _chunk_size2d = TerrainChunkSize::RECT_SIZE; let (base_z, sim_chunk) = match self @@ -145,7 +174,7 @@ impl World { }; let (min_z, only_structures_min_z, max_z) = - z_cache.get_z_limits(&mut sampler, &self.index); + z_cache.get_z_limits(&mut sampler, index); (base_z..min_z as i32).for_each(|z| { let _ = chunk.set(Vec3::new(x, y, z), stone); @@ -157,7 +186,7 @@ impl World { let only_structures = lpos.z >= only_structures_min_z as i32; if let Some(block) = - sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, &self.index) + sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, index) { let _ = chunk.set(lpos, block); } @@ -176,13 +205,13 @@ impl World { let mut rng = rand::thread_rng(); // Apply layers (paths, caves, etc.) - layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, &self.index, sim_chunk); - layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); - layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); + layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, index, sim_chunk); + layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, index); + layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, index); // Apply site generation sim_chunk.sites.iter().for_each(|site| { - self.index.sites[*site].apply_to(chunk_wpos2d, sample_get, &mut chunk) + index.sites[*site].apply_to(index, chunk_wpos2d, sample_get, &mut chunk) }); let gen_entity_pos = || { @@ -235,18 +264,13 @@ impl World { chunk_wpos2d, sample_get, &chunk, - &self.index, + index, &mut supplement, ); // Apply site supplementary information sim_chunk.sites.iter().for_each(|site| { - self.index.sites[*site].apply_supplement( - &mut rng, - chunk_wpos2d, - sample_get, - &mut supplement, - ) + index.sites[*site].apply_supplement(&mut rng, chunk_wpos2d, sample_get, &mut supplement) }); Ok((chunk, supplement)) diff --git a/world/src/sim/erosion.rs b/world/src/sim/erosion.rs index 19b946982b..e5128c60b5 100644 --- a/world/src/sim/erosion.rs +++ b/world/src/sim/erosion.rs @@ -1,9 +1,12 @@ -use super::{ - diffusion, downhill, neighbors, uniform_idx_as_vec2, uphill, vec2_as_uniform_idx, - NEIGHBOR_DELTA, WORLD_SIZE, -}; +use super::{diffusion, downhill, uphill}; use crate::{config::CONFIG, util::RandomField}; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + terrain::{ + neighbors, uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize, + NEIGHBOR_DELTA, + }, + vol::RectVolSize, +}; use tracing::{debug, error, warn}; // use faster::*; use itertools::izip; @@ -27,7 +30,12 @@ pub type Computex8 = [Compute; 8]; /// Compute the water flux at all chunks, given a list of chunk indices sorted /// by increasing height. -pub fn get_drainage(newh: &[u32], downhill: &[isize], _boundary_len: usize) -> Box<[f32]> { +pub fn get_drainage( + map_size_lg: MapSizeLg, + newh: &[u32], + downhill: &[isize], + _boundary_len: usize, +) -> Box<[f32]> { // FIXME: Make the below work. For now, we just use constant flux. // Initially, flux is determined by rainfall. We currently treat this as the // same as humidity, so we just use humidity as a proxy. The total flux @@ -35,10 +43,10 @@ pub fn get_drainage(newh: &[u32], downhill: &[isize], _boundary_len: usize) -> B // to be 0.5. To figure out how far from normal any given chunk is, we use // its logit. NOTE: If there are no non-boundary chunks, we just set // base_flux to 1.0; this should still work fine because in that case - // there's no erosion anyway. let base_flux = 1.0 / ((WORLD_SIZE.x * - // WORLD_SIZE.y) as f32); + // there's no erosion anyway. let base_flux = 1.0 / ((map_size_lg.chunks_len()) + // as f32); let base_flux = 1.0; - let mut flux = vec![base_flux; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut flux = vec![base_flux; map_size_lg.chunks_len()].into_boxed_slice(); newh.iter().rev().for_each(|&chunk_idx| { let chunk_idx = chunk_idx as usize; let downhill_idx = downhill[chunk_idx]; @@ -52,6 +60,7 @@ pub fn get_drainage(newh: &[u32], downhill: &[isize], _boundary_len: usize) -> B /// Compute the water flux at all chunks for multiple receivers, given a list of /// chunk indices sorted by increasing height and weights for each receiver. pub fn get_multi_drainage( + map_size_lg: MapSizeLg, mstack: &[u32], mrec: &[u8], mwrec: &[Computex8], @@ -66,12 +75,12 @@ pub fn get_multi_drainage( // base_flux to 1.0; this should still work fine because in that case // there's no erosion anyway. let base_area = 1.0; - let mut area = vec![base_area; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut area = vec![base_area; map_size_lg.chunks_len()].into_boxed_slice(); mstack.iter().for_each(|&ij| { let ij = ij as usize; let wrec_ij = &mwrec[ij]; let area_ij = area[ij]; - mrec_downhill(mrec, ij).for_each(|(k, ijr)| { + mrec_downhill(map_size_lg, mrec, ij).for_each(|(k, ijr)| { area[ijr] += area_ij * wrec_ij[k]; }); }); @@ -111,11 +120,11 @@ pub enum RiverKind { } impl RiverKind { - pub fn is_ocean(&self) -> bool { matches!(self, RiverKind::Ocean) } + pub fn is_ocean(&self) -> bool { matches!(*self, RiverKind::Ocean) } - pub fn is_river(&self) -> bool { matches!(self, RiverKind::River { .. }) } + pub fn is_river(&self) -> bool { matches!(*self, RiverKind::River { .. }) } - pub fn is_lake(&self) -> bool { matches!(self, RiverKind::Lake { .. }) } + pub fn is_lake(&self) -> bool { matches!(*self, RiverKind::Lake { .. }) } } impl PartialOrd for RiverKind { @@ -208,6 +217,8 @@ impl RiverData { /// liberties with the constant factors etc. in order to make it more likely /// that we draw rivers at all. pub fn get_rivers, G: Float + Into>( + map_size_lg: MapSizeLg, + continent_scale_hack: f64, newh: &[u32], water_alt: &[F], downhill: &[isize], @@ -218,7 +229,7 @@ pub fn get_rivers, G: Float + Into>( // to build up the derivatives from the top down. Fortunately this // computation seems tractable. - let mut rivers = vec![RiverData::default(); WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut rivers = vec![RiverData::default(); map_size_lg.chunks_len()].into_boxed_slice(); let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); // (Roughly) area of a chunk, times minutes per second. // NOTE: Clearly, this should "actually" be 1/60 (or maybe 1/64, if you want to @@ -229,7 +240,7 @@ pub fn get_rivers, G: Float + Into>( // increases, mins_per_sec should decrease, until it hits 1 / 60 or 1/ 64. // For example, if grid_scale is multiplied by 4, mins_per_sec should be // multiplied by 1 / (4.0 * 4.0). - let mins_per_sec = 1.0/*1.0 / 16.0*//*1.0 / 64.0*/; + let mins_per_sec = 1.0 / (continent_scale_hack * continent_scale_hack)/*1.0 / 16.0*//*1.0 / 64.0*/; let chunk_area_factor = neighbor_coef.x * neighbor_coef.y * mins_per_sec; // NOTE: This technically makes us discontinuous, so we should be cautious about // using this. @@ -244,8 +255,8 @@ pub fn get_rivers, G: Float + Into>( return; } let downhill_idx = downhill_idx as usize; - let downhill_pos = uniform_idx_as_vec2(downhill_idx); - let dxy = (downhill_pos - uniform_idx_as_vec2(chunk_idx)).map(|e| e as f64); + let downhill_pos = uniform_idx_as_vec2(map_size_lg, downhill_idx); + let dxy = (downhill_pos - uniform_idx_as_vec2(map_size_lg, chunk_idx)).map(|e| e as f64); let neighbor_dim = neighbor_coef * dxy; // First, we calculate the river's volumetric flow rate. let chunk_drainage = drainage[chunk_idx].into(); @@ -364,10 +375,16 @@ pub fn get_rivers, G: Float + Into>( // This is a pass, so set our flow direction to point to the neighbor pass // rather than downhill. // NOTE: Safe because neighbor_pass_idx >= 0. - (uniform_idx_as_vec2(downhill_idx), river_spline_derivative) + ( + uniform_idx_as_vec2(map_size_lg, downhill_idx), + river_spline_derivative, + ) } else { // Try pointing towards the lake side of the pass. - (uniform_idx_as_vec2(pass_idx), river_spline_derivative) + ( + uniform_idx_as_vec2(map_size_lg, pass_idx), + river_spline_derivative, + ) }; let mut lake = &mut rivers[chunk_idx]; lake.spline_derivative = river_spline_derivative; @@ -409,12 +426,12 @@ pub fn get_rivers, G: Float + Into>( error!( "Our chunk (and downhill, lake, pass, neighbor_pass): {:?} (to {:?}, in {:?} via \ {:?} to {:?}), chunk water alt: {:?}, lake water alt: {:?}", - uniform_idx_as_vec2(chunk_idx), - uniform_idx_as_vec2(downhill_idx), - uniform_idx_as_vec2(lake_idx), - uniform_idx_as_vec2(pass_idx), + uniform_idx_as_vec2(map_size_lg, chunk_idx), + uniform_idx_as_vec2(map_size_lg, downhill_idx), + uniform_idx_as_vec2(map_size_lg, lake_idx), + uniform_idx_as_vec2(map_size_lg, pass_idx), if neighbor_pass_idx >= 0 { - Some(uniform_idx_as_vec2(neighbor_pass_idx as usize)) + Some(uniform_idx_as_vec2(map_size_lg, neighbor_pass_idx as usize)) } else { None }, @@ -532,6 +549,7 @@ pub fn get_rivers, G: Float + Into>( /// TODO: See if allocating in advance is worthwhile. #[allow(clippy::let_and_return)] // TODO: Pending review in #587 fn get_max_slope( + map_size_lg: MapSizeLg, h: &[Alt], rock_strength_nz: &(impl NoiseFn> + Sync), height_scale: impl Fn(usize) -> Alt + Sync, @@ -542,7 +560,7 @@ fn get_max_slope( h.par_iter() .enumerate() .map(|(posi, &z)| { - let wposf = uniform_idx_as_vec2(posi).map(|e| e as f64) + let wposf = uniform_idx_as_vec2(map_size_lg, posi).map(|e| e as f64) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); let height_scale = height_scale(posi); let wposz = z as f64 / height_scale as f64; @@ -667,6 +685,8 @@ fn get_max_slope( #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] fn erode( + // Underlying map dimensions. + map_size_lg: MapSizeLg, // Height above sea level of topsoil h: &mut [Alt], // Height above sea level of bedrock @@ -715,8 +735,8 @@ fn erode( // at the cost of less interesting erosion behavior (linear vs. nonlinear). let q_ = 1.5; let k_da = 2.5 * k_da_scale(q); - let nx = WORLD_SIZE.x; - let ny = WORLD_SIZE.y; + let nx = usize::from(map_size_lg.chunks().x); + let ny = usize::from(map_size_lg.chunks().y); let dx = TerrainChunkSize::RECT_SIZE.x as f64; let dy = TerrainChunkSize::RECT_SIZE.y as f64; @@ -777,11 +797,17 @@ fn erode( let g_fs_mult_sed = 1.0; let ((dh, newh, maxh, mrec, mstack, mwrec, area), (mut max_slopes, h_t)) = rayon::join( || { - let mut dh = downhill(|posi| h[posi], |posi| is_ocean(posi) && h[posi] <= 0.0); + let mut dh = downhill( + map_size_lg, + |posi| h[posi], + |posi| is_ocean(posi) && h[posi] <= 0.0, + ); debug!("Computed downhill..."); - let (boundary_len, _indirection, newh, maxh) = get_lakes(|posi| h[posi], &mut dh); + let (boundary_len, _indirection, newh, maxh) = + get_lakes(map_size_lg, |posi| h[posi], &mut dh); debug!("Got lakes..."); let (mrec, mstack, mwrec) = get_multi_rec( + map_size_lg, |posi| h[posi], &dh, &newh, @@ -795,16 +821,17 @@ fn erode( debug!("Got multiple receivers..."); // TODO: Figure out how to switch between single-receiver and multi-receiver // drainage, as the former is much less computationally costly. - // let area = get_drainage(&newh, &dh, boundary_len); - let area = get_multi_drainage(&mstack, &mrec, &*mwrec, boundary_len); + // let area = get_drainage(map_size_lg, &newh, &dh, boundary_len); + let area = get_multi_drainage(map_size_lg, &mstack, &mrec, &*mwrec, boundary_len); debug!("Got flux..."); (dh, newh, maxh, mrec, mstack, mwrec, area) }, || { rayon::join( || { - let max_slope = - get_max_slope(h, rock_strength_nz, |posi| height_scale(n_f(posi))); + let max_slope = get_max_slope(map_size_lg, h, rock_strength_nz, |posi| { + height_scale(n_f(posi)) + }); debug!("Got max slopes..."); max_slope }, @@ -852,9 +879,10 @@ fn erode( let m = m_f(posi) as f64; let mwrec_i = &mwrec[posi]; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)) - .map(|e| e as f64); + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let knew = (k * (p as f64 @@ -901,7 +929,7 @@ fn erode( let k_da = k_da * kd_factor; let mwrec_i = &mwrec[posi]; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { let mwrec_kk = mwrec_i[kk] as f64; #[rustfmt::skip] @@ -947,8 +975,9 @@ fn erode( let k = (mwrec_kk * (uplift_i + max_uplift as f64 * g_i / p as f64)) / (1.0 + k_da * (mwrec_kk * chunk_neutral_area).powf(q)) / max_slope.powf(q_); - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)) - .map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let knew = (k @@ -966,11 +995,10 @@ fn erode( debug!("Computed stream power factors..."); - let mut lake_water_volume = - vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut elev = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut h_p = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut deltah = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut lake_water_volume = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut elev = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut h_p = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut deltah = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); // calculate the elevation / SPL, including sediment flux let tol1 = 1.0e-4 as Compute * (maxh as Compute + 1.0); @@ -1004,8 +1032,8 @@ fn erode( // Gauss-Seidel iteration - let mut lake_silt = vec![0.0 as Compute; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); - let mut lake_sill = vec![-1isize; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut lake_silt = vec![0.0 as Compute; map_size_lg.chunks_len()].into_boxed_slice(); + let mut lake_sill = vec![-1isize; map_size_lg.chunks_len()].into_boxed_slice(); let mut n_gs_stream_power_law = 0; // NOTE: Increasing this can theoretically sometimes be necessary for @@ -1102,7 +1130,7 @@ fn erode( } } let mwrec_i = &mwrec[posi]; - mrec_downhill(&mrec, posi).for_each(|(k, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(k, posj)| { let stack_posj = mstack_inv[posj]; deltah[stack_posj] += deltah_i * mwrec_i[k]; }); @@ -1251,7 +1279,7 @@ fn erode( if (n - 1.0).abs() <= 1.0e-3 && (q_ - 1.0).abs() <= 1.0e-3 { let mut f = h0; let mut df = 1.0; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { let posj_stack = mstack_inv[posj]; let h_j = h_stack[posj_stack] as f64; // This can happen in cases where receiver kk is neither uphill of @@ -1277,7 +1305,7 @@ fn erode( let mut errp = 2.0 * tolp; let mut rec_heights = [0.0; 8]; let mut mask = [MaskType::new(false); 8]; - mrec_downhill(&mrec, posi).for_each(|(kk, posj)| { + mrec_downhill(map_size_lg, &mrec, posi).for_each(|(kk, posj)| { let posj_stack = mstack_inv[posj]; let h_j = h_stack[posj_stack]; // NOTE: Fastscape does h_t[posi] + uplift(posi) as f64 >= h_t[posj] @@ -1365,8 +1393,9 @@ fn erode( // of wh_j. new_h_i = wh_j; } else if compute_stats && new_h_i > 0.0 { - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)) - .map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let dz = (new_h_i - wh_j).max(0.0); let mag_slope = dz / neighbor_distance; @@ -1461,7 +1490,9 @@ fn erode( } else { let posj = posj as usize; let h_j = h[posj]; - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)).map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posj)) + .map(|e| e as f64); let neighbor_distance_squared = (neighbor_coef * dxy).magnitude_squared(); let dh = (h_i - h_j) as f64; // H_i_fact = (h_i - h_j) / (||p_i - p_j||^2 + (h_i - h_j)^2) @@ -1590,7 +1621,9 @@ fn erode( // redistribute uplift to other neighbors. let (posk, h_k) = (posj, h_j); let (posk, h_k) = if h_k < h_j { (posk, h_k) } else { (posj, h_j) }; - let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posk)).map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, posi) + - uniform_idx_as_vec2(map_size_lg, posk)) + .map(|e| e as f64); let neighbor_distance = (neighbor_coef * dxy).magnitude(); let dz = (new_h_i - h_k).max(0.0); let mag_slope = dz / neighbor_distance; @@ -1688,6 +1721,7 @@ fn erode( /// /// See https://github.com/mewo2/terrain/blob/master/terrain.js pub fn fill_sinks( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F + Sync, is_ocean: impl Fn(usize) -> bool + Sync, ) -> Box<[F]> { @@ -1695,7 +1729,7 @@ pub fn fill_sinks( // but doesn't change altitudes. let epsilon = F::zero(); let infinity = F::infinity(); - let range = 0..WORLD_SIZE.x * WORLD_SIZE.y; + let range = 0..map_size_lg.chunks_len(); let oldh = range .into_par_iter() .map(|posi| h(posi)) @@ -1724,7 +1758,7 @@ pub fn fill_sinks( if nh == oh { return; } - for nposi in neighbors(posi) { + for nposi in neighbors(map_size_lg, posi) { let onbh = newh[nposi]; let nbh = onbh + epsilon; // If there is even one path downhill from this node's original height, fix @@ -1775,6 +1809,7 @@ pub fn fill_sinks( /// indirection vector. #[allow(clippy::filter_next)] // TODO: Pending review in #587 pub fn get_lakes( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F, downhill: &mut [isize], ) -> (usize, Box<[i32]>, Box<[u32]>, F) { @@ -1799,7 +1834,7 @@ pub fn get_lakes( // node, so we should access that entry and increment it, then set our own // entry to it. let mut boundary = Vec::with_capacity(downhill.len()); - let mut indirection = vec![/*-1i32*/0i32; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut indirection = vec![/*-1i32*/0i32; map_size_lg.chunks_len()].into_boxed_slice(); let mut newh = Vec::with_capacity(downhill.len()); @@ -1837,7 +1872,7 @@ pub fn get_lakes( let mut cur = start; while cur < newh.len() { let node = newh[cur as usize]; - uphill(downhill, node as usize).for_each(|child| { + uphill(map_size_lg, downhill, node as usize).for_each(|child| { // lake_idx is the index of our lake root. indirection[child] = chunk_idx as i32; indirection_[child] = indirection_idx; @@ -1878,7 +1913,7 @@ pub fn get_lakes( // our own lake's entry list. If the maximum of the heights we get out // from this process is greater than the maximum of this chunk and its // neighbor chunk, we switch to this new edge. - neighbors(chunk_idx).for_each(|neighbor_idx| { + neighbors(map_size_lg, chunk_idx).for_each(|neighbor_idx| { let neighbor_height = h(neighbor_idx); let neighbor_lake_idx_ = indirection_[neighbor_idx]; let neighbor_lake_idx = neighbor_lake_idx_ as usize; @@ -1944,24 +1979,33 @@ pub fn get_lakes( "For edge {:?} between lakes {:?}, couldn't find partner for pass \ {:?}. Should never happen; maybe forgot to set both edges?", ( - (chunk_idx, uniform_idx_as_vec2(chunk_idx as usize)), - (neighbor_idx, uniform_idx_as_vec2(neighbor_idx as usize)) + ( + chunk_idx, + uniform_idx_as_vec2(map_size_lg, chunk_idx as usize) + ), + ( + neighbor_idx, + uniform_idx_as_vec2(map_size_lg, neighbor_idx as usize) + ) ), ( ( lake_chunk_idx, - uniform_idx_as_vec2(lake_chunk_idx as usize), + uniform_idx_as_vec2(map_size_lg, lake_chunk_idx as usize), lake_idx_ ), ( neighbor_lake_chunk_idx, - uniform_idx_as_vec2(neighbor_lake_chunk_idx as usize), + uniform_idx_as_vec2( + map_size_lg, + neighbor_lake_chunk_idx as usize + ), neighbor_lake_idx_ ) ), ( - (pass.0, uniform_idx_as_vec2(pass.0 as usize)), - (pass.1, uniform_idx_as_vec2(pass.1 as usize)) + (pass.0, uniform_idx_as_vec2(map_size_lg, pass.0 as usize)), + (pass.1, uniform_idx_as_vec2(map_size_lg, pass.1 as usize)) ), ); } @@ -2050,7 +2094,7 @@ pub fn get_lakes( downhill[lake_chunk_idx] = neighbor_idx as isize; // Also set the indirection of the lake bottom to the negation of the // lake side of the chosen pass (chunk_idx). - // NOTE: This can't overflow i32 because WORLD_SIZE.x * WORLD_SIZE.y should fit + // NOTE: This can't overflow i32 because map_size_lg.chunks_len() should fit // in an i32. indirection[lake_chunk_idx] = -(chunk_idx as i32); // Add this edge to the sorted list. @@ -2096,7 +2140,7 @@ pub fn get_lakes( InQueue, WithRcv, } - let mut tag = vec![Tag::UnParsed; WORLD_SIZE.x * WORLD_SIZE.y]; + let mut tag = vec![Tag::UnParsed; map_size_lg.chunks_len()]; // TODO: Combine with adding to vector. let mut filling_queue = Vec::with_capacity(downhill.len()); @@ -2164,17 +2208,17 @@ pub fn get_lakes( tag[neighbor_pass_idx] = Tag::WithRcv; tag[pass_idx] = Tag::InQueue; - let outflow_coords = uniform_idx_as_vec2(neighbor_pass_idx); + let outflow_coords = uniform_idx_as_vec2(map_size_lg, neighbor_pass_idx); let elev = h(neighbor_pass_idx).max(h(pass_idx)); while let Some(node) = filling_queue.pop() { - let coords = uniform_idx_as_vec2(node); + let coords = uniform_idx_as_vec2(map_size_lg, node); let mut rcv = -1; let mut rcv_cost = -f64::INFINITY; /*f64::EPSILON;*/ let outflow_distance = (outflow_coords - coords).map(|e| e as f64); - neighbors(node).for_each(|ineighbor| { + neighbors(map_size_lg, node).for_each(|ineighbor| { if indirection[ineighbor] != chunk_idx as i32 && ineighbor != chunk_idx && ineighbor != neighbor_pass_idx @@ -2182,7 +2226,8 @@ pub fn get_lakes( { return; } - let dxy = (uniform_idx_as_vec2(ineighbor) - coords).map(|e| e as f64); + let dxy = (uniform_idx_as_vec2(map_size_lg, ineighbor) - coords) + .map(|e| e as f64); let neighbor_distance = /*neighbor_coef * */dxy; let tag = &mut tag[ineighbor]; match *tag { @@ -2224,7 +2269,7 @@ pub fn get_lakes( let mut cur = start; let mut node = first_idx; loop { - uphill(downhill, node as usize).for_each(|child| { + uphill(map_size_lg, downhill, node as usize).for_each(|child| { // lake_idx is the index of our lake root. // Check to make sure child (flowing into us) is in the same lake. if indirection[child] == chunk_idx as i32 || child == chunk_idx { @@ -2245,10 +2290,11 @@ pub fn get_lakes( /// Iterate through set neighbors of multi-receiver flow. pub fn mrec_downhill<'a>( + map_size_lg: MapSizeLg, mrec: &'a [u8], posi: usize, ) -> impl Clone + Iterator + 'a { - let pos = uniform_idx_as_vec2(posi); + let pos = uniform_idx_as_vec2(map_size_lg, posi); let mrec_i = mrec[posi]; NEIGHBOR_DELTA .iter() @@ -2257,13 +2303,14 @@ pub fn mrec_downhill<'a>( .map(move |(k, &(x, y))| { ( k, - vec2_as_uniform_idx(Vec2::new(pos.x + x as i32, pos.y + y as i32)), + vec2_as_uniform_idx(map_size_lg, Vec2::new(pos.x + x as i32, pos.y + y as i32)), ) }) } /// Algorithm for computing multi-receiver flow. /// +/// * `map_size_lg`: Size of the underlying map. /// * `h`: altitude /// * `downhill`: single receiver /// * `newh`: single receiver stack @@ -2281,6 +2328,7 @@ pub fn mrec_downhill<'a>( #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] // TODO: Pending review in #587 pub fn get_multi_rec>( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F + Sync, downhill: &[isize], newh: &[u32], @@ -2361,18 +2409,15 @@ pub fn get_multi_rec>( let mut mrec_ij = 0u8; let mut ndon_ij = 0u8; let neighbor_iter = |posi| { - let pos = uniform_idx_as_vec2(posi); + let pos = uniform_idx_as_vec2(map_size_lg, posi); NEIGHBOR_DELTA .iter() .map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y)) .enumerate() .filter(move |&(_, pos)| { - pos.x >= 0 - && pos.y >= 0 - && pos.x < WORLD_SIZE.x as i32 - && pos.y < WORLD_SIZE.y as i32 + pos.x >= 0 && pos.y >= 0 && pos.x < nx as i32 && pos.y < ny as i32 }) - .map(move |(k, pos)| (k, vec2_as_uniform_idx(pos))) + .map(move |(k, pos)| (k, vec2_as_uniform_idx(map_size_lg, pos))) }; neighbor_iter(ij).for_each(|(k, ijk)| { @@ -2399,9 +2444,10 @@ pub fn get_multi_rec>( let mut sumweight = czero; let mut wrec = [czero; 8]; let mut nrec = 0; - mrec_downhill(&mrec, ij).for_each(|(k, ijk)| { - let lrec_ijk = ((uniform_idx_as_vec2(ijk) - uniform_idx_as_vec2(ij)) - .map(|e| e as Compute) + mrec_downhill(map_size_lg, &mrec, ij).for_each(|(k, ijk)| { + let lrec_ijk = ((uniform_idx_as_vec2(map_size_lg, ijk) + - uniform_idx_as_vec2(map_size_lg, ij)) + .map(|e| e as Compute) * dxdy) .magnitude(); let wrec_ijk = (wh[ij] - wh[ijk]).into() / lrec_ijk; @@ -2446,7 +2492,7 @@ pub fn get_multi_rec>( while let Some(ijn) = parse.pop() { // we add the node to the stack stack.push(ijn as u32); - mrec_downhill(&mrec, ijn).for_each(|(_, ijr)| { + mrec_downhill(map_size_lg, &mrec, ijn).for_each(|(_, ijr)| { let (_, ref mut vis_ijr) = don_vis[ijr]; if *vis_ijr >= 1 { *vis_ijr -= 1; @@ -2474,6 +2520,7 @@ pub fn get_multi_rec>( #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] pub fn do_erosion( + map_size_lg: MapSizeLg, _max_uplift: f32, n_steps: usize, seed: &RandomField, @@ -2495,59 +2542,59 @@ pub fn do_erosion( k_da_scale: impl Fn(f64) -> f64, ) -> (Box<[Alt]>, Box<[Alt]> /* , Box<[Alt]> */) { debug!("Initializing erosion arrays..."); - let oldh_ = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let oldh_ = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| oldh(posi) as Alt) .collect::>() .into_boxed_slice(); // Topographic basement (The height of bedrock, not including sediment). - let mut b = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let mut b = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| oldb(posi) as Alt) .collect::>() .into_boxed_slice(); // Stream power law slope exponent--link between channel slope and erosion rate. - let n = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let n = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| n(posi)) .collect::>() .into_boxed_slice(); // Stream power law concavity index (θ = m/n), turned into an exponent on // drainage (which is a proxy for discharge according to Hack's Law). - let m = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let m = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| theta(posi) * n[posi]) .collect::>() .into_boxed_slice(); // Stream power law erodability constant for fluvial erosion (bedrock) - let kf = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let kf = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| kf(posi)) .collect::>() .into_boxed_slice(); // Stream power law erodability constant for hillslope diffusion (bedrock) - let kd = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let kd = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| kd(posi)) .collect::>() .into_boxed_slice(); // Deposition coefficient - let g = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let g = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| g(posi)) .collect::>() .into_boxed_slice(); - let epsilon_0 = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let epsilon_0 = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| epsilon_0(posi)) .collect::>() .into_boxed_slice(); - let alpha = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let alpha = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| alpha(posi)) .collect::>() .into_boxed_slice(); - let mut wh = vec![0.0; WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut wh = vec![0.0; map_size_lg.chunks_len()].into_boxed_slice(); // TODO: Don't do this, maybe? // (To elaborate, maybe we should have varying uplift or compute it some other // way). @@ -2595,6 +2642,7 @@ pub fn do_erosion( (0..n_steps).for_each(|i| { debug!("Erosion iteration #{:?}", i); erode( + map_size_lg, &mut h, &mut b, &mut wh, diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 2dee374295..02254e01a1 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -1,340 +1,274 @@ use crate::{ - sim::{RiverKind, WorldSim, WORLD_SIZE}, - Index, CONFIG, + column::ColumnSample, + sim::{RiverKind, WorldSim}, + IndexRef, CONFIG, +}; +use common::{ + terrain::{ + map::{Connection, ConnectionKind, MapConfig, MapSample}, + vec2_as_uniform_idx, TerrainChunkSize, NEIGHBOR_DELTA, + }, + vol::RectVolSize, }; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use std::{f32, f64}; use vek::*; -pub struct MapConfig { - /// Dimensions of the window being written to. Defaults to WORLD_SIZE. - pub dimensions: Vec2, - /// x, y, and z of top left of map (defaults to (0.0, 0.0, - /// CONFIG.sea_level)). - pub focus: Vec3, - /// Altitude is divided by gain and clamped to [0, 1]; thus, decreasing gain - /// makes smaller differences in altitude appear larger. - /// - /// Defaults to CONFIG.mountain_scale. - pub gain: f32, - /// lgain is used for shading purposes and refers to how much impact a - /// change in the z direction has on the perceived slope relative to the - /// same change in x and y. - /// - /// Defaults to TerrainChunkSize::RECT_SIZE.x. - pub lgain: f64, - /// Scale is like gain, but for x and y rather than z. - /// - /// Defaults to WORLD_SIZE.x / dimensions.x (NOTE: fractional, not integer, - /// division!). - pub scale: f64, - /// Vector that indicates which direction light is coming from, if shading - /// is turned on. - /// - /// Right-handed coordinate system: light is going left, down, and - /// "backwards" (i.e. on the map, where we translate the y coordinate on - /// the world map to z in the coordinate system, the light comes from -y - /// on the map and points towards +y on the map). In a right - /// handed coordinate system, the "camera" points towards -z, so positive z - /// is backwards "into" the camera. - /// - /// "In world space the x-axis will be pointing east, the y-axis up and the - /// z-axis will be pointing south" - /// - /// Defaults to (-0.8, -1.0, 0.3). - pub light_direction: Vec3, - /// If true, only the basement (bedrock) is used for altitude; otherwise, - /// the surface is used. - /// - /// Defaults to false. - pub is_basement: bool, - /// If true, water is rendered; otherwise, the surface without water is - /// rendered, even if it is underwater. - /// - /// Defaults to true. - pub is_water: bool, - /// If true, 3D lighting and shading are turned on. Otherwise, a plain - /// altitude map is used. - /// - /// Defaults to true. - pub is_shaded: bool, - /// If true, the red component of the image is also used for temperature - /// (redder is hotter). Defaults to false. - pub is_temperature: bool, - /// If true, the blue component of the image is also used for humidity - /// (bluer is wetter). - /// - /// Defaults to false. - pub is_humidity: bool, - /// Record debug information. - /// - /// Defaults to false. - pub is_debug: bool, -} +/// A sample function that grabs the connections at a chunk. +/// +/// Currently this just supports rivers, but ideally it can be extended past +/// that. +/// +/// A sample function that grabs surface altitude at a column. +/// (correctly reflecting settings like is_basement and is_water). +/// +/// The altitude produced by this function at a column corresponding to a +/// particular chunk should be identical to the altitude produced by +/// sample_pos at that chunk. +/// +/// You should generally pass a closure over this function into generate +/// when constructing a map for the first time. +/// However, if repeated construction is needed, or alternate base colors +/// are to be used for some reason, one should pass a custom function to +/// generate instead (e.g. one that just looks up the height in a cached +/// array). +pub fn sample_wpos(config: &MapConfig, sampler: &WorldSim, wpos: Vec2) -> f32 { + let MapConfig { + focus, + gain, -pub const QUADRANTS: usize = 4; + is_basement, + is_water, + .. + } = *config; -pub struct MapDebug { - pub quads: [[u32; QUADRANTS]; QUADRANTS], - pub rivers: u32, - pub lakes: u32, - pub oceans: u32, -} - -impl Default for MapConfig { - fn default() -> Self { - let dimensions = WORLD_SIZE; - Self { - dimensions, - focus: Vec3::new(0.0, 0.0, CONFIG.sea_level as f64), - gain: CONFIG.mountain_scale, - lgain: TerrainChunkSize::RECT_SIZE.x as f64, - scale: WORLD_SIZE.x as f64 / dimensions.x as f64, - light_direction: Vec3::new(-0.8, -1.0, 0.3), - - is_basement: false, - is_water: true, - is_shaded: true, - is_temperature: false, - is_humidity: false, - is_debug: false, - } - } -} - -impl MapConfig { - /// Generates a map image using the specified settings. Note that it will - /// write from left to write from (0, 0) to dimensions - 1, inclusive, - /// with 4 1-byte color components provided as (r, g, b, a). It is up - /// to the caller to provide a function that translates this information - /// into the correct format for a buffer and writes to it. - #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 - #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 - #[allow(clippy::map_identity)] // TODO: Pending review in #587 - #[allow(clippy::many_single_char_names)] - pub fn generate( - &self, - sampler: &WorldSim, - index: &Index, - mut write_pixel: impl FnMut(Vec2, (u8, u8, u8, u8)), - ) -> MapDebug { - let MapConfig { - dimensions, - focus, - gain, - lgain, - scale, - light_direction, - - is_basement, - is_water, - is_shaded, - is_temperature, - is_humidity, - is_debug, - } = *self; - - let light = light_direction.normalized(); - let mut quads = [[0u32; QUADRANTS]; QUADRANTS]; - let mut rivers = 0u32; - let mut lakes = 0u32; - let mut oceans = 0u32; - - let focus_rect = Vec2::from(focus); - let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64; - - (0..dimensions.y * dimensions.x).for_each(|chunk_idx| { - let i = chunk_idx % dimensions.x as usize; - let j = chunk_idx / dimensions.x as usize; - - let pos = (focus_rect + Vec2::new(i as f64, j as f64) * scale).map(|e: f64| e as i32); - - let ( - alt, - basement, - water_alt, - humidity, - temperature, - downhill, - river_kind, - is_path, - is_cave, - near_site, - ) = sampler - .get(pos) - .map(|sample| { - ( - sample.alt, - sample.basement, - sample.water_alt, - sample.humidity, - sample.temp, - sample.downhill, - sample.river.river_kind, - sample.path.0.is_way(), - sample.cave.0.is_way(), - sample.sites.iter().any(|site| { - index.sites[*site] - .get_origin() - .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) - < 64i32.pow(2) - }), - ) - }) - .unwrap_or(( - CONFIG.sea_level, - CONFIG.sea_level, - CONFIG.sea_level, - 0.0, - 0.0, - None, - None, - false, - false, - false, - )); - let humidity = humidity.min(1.0).max(0.0); - let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; - let pos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); - let downhill_pos = (downhill - .map(|downhill_pos| downhill_pos) - .unwrap_or(pos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - - pos) - + pos; - let downhill_alt = sampler - .get_wpos(downhill_pos) - .map(|s| if is_basement { s.basement } else { s.alt }) - .unwrap_or(CONFIG.sea_level); - let alt = if is_basement { basement } else { alt }; - let cross_pos = pos - + ((downhill_pos - pos) - .map(|e| e as f32) - .rotated_z(f32::consts::FRAC_PI_2) - .map(|e| e as i32)); - let cross_alt = sampler - .get_wpos(cross_pos) - .map(|s| if is_basement { s.basement } else { s.alt }) - .unwrap_or(CONFIG.sea_level); - // Pointing downhill, forward - // (index--note that (0,0,1) is backward right-handed) - let forward_vec = Vec3::new( - (downhill_pos.x - pos.x) as f64, - (downhill_alt - alt) as f64 * lgain, - (downhill_pos.y - pos.y) as f64, - ); - // Pointing 90 degrees left (in horizontal xy) of downhill, up - // (middle--note that (1,0,0), 90 degrees CCW backward, is right right-handed) - let up_vec = Vec3::new( - (cross_pos.x - pos.x) as f64, - (cross_alt - alt) as f64 * lgain, - (cross_pos.y - pos.y) as f64, - ); - // Then cross points "to the right" (upwards) on a right-handed coordinate - // system. (right-handed coordinate system means (0, 0, 1.0) is - // "forward" into the screen). - let surface_normal = forward_vec.cross(up_vec).normalized(); - let light = (surface_normal.dot(light) + 1.0) / 2.0; - let light = (light * 0.9) + 0.1; - - let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; - let true_alt = (alt as f64 - focus.z) / gain as f64; - let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0); - let water_alt = true_water_alt.min(1.0).max(0.0); - let alt = true_alt.min(1.0).max(0.0); - if is_debug { - let quad = - |x: f32| ((x as f64 * QUADRANTS as f64).floor() as usize).min(QUADRANTS - 1); - if river_kind.is_none() || humidity != 0.0 { - quads[quad(humidity)][quad(temperature)] += 1; - } - match river_kind { - Some(RiverKind::River { .. }) => { - rivers += 1; - }, - Some(RiverKind::Lake { .. }) => { - lakes += 1; - }, - Some(RiverKind::Ocean { .. }) => { - oceans += 1; - }, - None => {}, - } - } - - let water_color_factor = 2.0; - let g_water = 32.0 * water_color_factor; - let b_water = 64.0 * water_color_factor; - let rgba = match (river_kind, (is_water, true_alt >= true_sea_level)) { - (_, (false, _)) | (None, (_, true)) => { - let (r, g, b) = ( - (if is_shaded { alt } else { alt } - * if is_temperature { - temperature as f64 - } else if is_shaded { - alt - } else { - 0.0 - }) - .sqrt(), - if is_shaded { 0.4 + (alt * 0.6) } else { alt }, - (if is_shaded { alt } else { alt } - * if is_humidity { - humidity as f64 - } else if is_shaded { - alt - } else { - 0.0 - }) - .sqrt(), - ); - let light = if is_shaded { light } else { 1.0 }; - ( - (r * light * 255.0) as u8, - (g * light * 255.0) as u8, - (b * light * 255.0) as u8, - 255, - ) - }, - (Some(RiverKind::Ocean), _) => ( - 0, - ((g_water - water_depth * g_water) * 1.0) as u8, - ((b_water - water_depth * b_water) * 1.0) as u8, - 255, - ), - (Some(RiverKind::River { .. }), _) => ( - 0, - g_water as u8 + (alt * (127.0 - g_water)) as u8, - b_water as u8 + (alt * (255.0 - b_water)) as u8, - 255, - ), - (None, _) | (Some(RiverKind::Lake { .. }), _) => ( - 0, - (((g_water + water_alt * (127.0 - 32.0)) + (-water_depth * g_water)) * 1.0) - as u8, - (((b_water + water_alt * (255.0 - b_water)) + (-water_depth * b_water)) * 1.0) - as u8, - 255, - ), - }; - - let rgba = if near_site { - (0x57, 0x39, 0x33, 0xFF) - } else if is_path { - (0x37, 0x29, 0x23, 0xFF) - } else if is_cave { - (0x37, 0x37, 0x37, 0xFF) + (sampler + .get_wpos(wpos) + .map(|s| { + if is_basement { s.basement } else { s.alt }.max(if is_water { + s.water_alt } else { - rgba - }; + -f32::INFINITY + }) + }) + .unwrap_or(CONFIG.sea_level) + - focus.z as f32) + / gain as f32 +} - write_pixel(Vec2::new(i, j), rgba); +/// Samples a MapSample at a chunk. +/// +/// You should generally pass a closure over this function into generate +/// when constructing a map for the first time. +/// However, if repeated construction is needed, or alternate base colors +/// are to be used for some reason, one should pass a custom function to +/// generate instead (e.g. one that just looks up the color in a cached +/// array). +// NOTE: Deliberately not putting Rgb colors here in the config file; they +// aren't hot reloaded anyway, and for various reasons they're probably not a +// good idea to update in that way (for example, we currently want water colors +// to match voxygen's). Eventually we'll fix these sorts of issues in some +// other way. +pub fn sample_pos( + config: &MapConfig, + sampler: &WorldSim, + index: IndexRef, + samples: Option<&[Option]>, + pos: Vec2, +) -> MapSample { + let map_size_lg = config.map_size_lg(); + let MapConfig { + focus, + gain, + + is_basement, + is_water, + is_shaded, + is_temperature, + is_humidity, + // is_debug, + .. + } = *config; + + let true_sea_level = (CONFIG.sea_level as f64 - focus.z) / gain as f64; + + let ( + chunk_idx, + alt, + basement, + water_alt, + humidity, + temperature, + downhill, + river_kind, + spline_derivative, + is_path, + is_cave, + near_site, + ) = sampler + .get(pos) + .map(|sample| { + ( + Some(vec2_as_uniform_idx(map_size_lg, pos)), + sample.alt, + sample.basement, + sample.water_alt, + sample.humidity, + sample.temp, + sample.downhill, + sample.river.river_kind, + sample.river.spline_derivative, + sample.path.0.is_way(), + sample.cave.0.is_way(), + sample.sites.iter().any(|site| { + index.sites[*site] + .get_origin() + .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) + < 64i32.pow(2) + }), + ) + }) + .unwrap_or(( + None, + CONFIG.sea_level, + CONFIG.sea_level, + CONFIG.sea_level, + 0.0, + 0.0, + None, + None, + Vec2::zero(), + false, + false, + false, + )); + + let humidity = humidity.min(1.0).max(0.0); + let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; + let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let column_rgb = samples + .and_then(|samples| { + chunk_idx + .and_then(|chunk_idx| samples.get(chunk_idx)) + .map(Option::as_ref) + .flatten() + }) + .map(|sample| { + // TODO: Eliminate the redundancy between this and the block renderer. + let alt = sample.alt; + let basement = sample.basement; + let grass_depth = (1.5 + 2.0 * sample.chaos).min(alt - basement); + let wposz = if is_basement { basement } else { alt }; + if is_basement && wposz < alt - grass_depth { + Lerp::lerp( + sample.sub_surface_color, + sample.stone_col.map(|e| e as f32 / 255.0), + (alt - grass_depth - wposz as f32) * 0.15, + ) + .map(|e| e as f64) + } else { + Lerp::lerp( + sample.sub_surface_color, + sample.surface_color, + ((wposz as f32 - (alt - grass_depth)) / grass_depth).powf(0.5), + ) + .map(|e| e as f64) + } }); - MapDebug { - quads, - rivers, - lakes, - oceans, - } + let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); + let alt = if is_basement { basement } else { alt }; + + let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; + let true_alt = (alt as f64 - focus.z) / gain as f64; + let water_depth = (true_water_alt - true_alt).min(1.0).max(0.0); + let alt = true_alt.min(1.0).max(0.0); + + let water_color_factor = 2.0; + let g_water = 32.0 * water_color_factor; + let b_water = 64.0 * water_color_factor; + let default_rgb = Rgb::new( + if is_shaded || is_temperature { + 1.0 + } else { + 0.0 + }, + if is_shaded { 1.0 } else { alt }, + if is_shaded || is_humidity { 1.0 } else { 0.0 }, + ); + let column_rgb = column_rgb.unwrap_or(default_rgb); + let mut connections = [None; 8]; + let mut has_connections = false; + // TODO: Support non-river connections. + // TODO: Support multiple connections. + let river_width = river_kind.map(|river| match river { + RiverKind::River { cross_section } => cross_section.x, + RiverKind::Lake { .. } | RiverKind::Ocean => TerrainChunkSize::RECT_SIZE.x as f32, + }); + if let (Some(river_width), true) = (river_width, is_water) { + let downhill_pos = downhill_wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + NEIGHBOR_DELTA + .iter() + .zip((&mut connections).iter_mut()) + .filter(|&(&offset, _)| downhill_pos - pos == Vec2::from(offset)) + .for_each(|(_, connection)| { + has_connections = true; + *connection = Some(Connection { + kind: ConnectionKind::River, + spline_derivative, + width: river_width, + }); + }); + }; + let rgb = match (river_kind, (is_water, true_alt >= true_sea_level)) { + (_, (false, _)) | (None, (_, true)) | (Some(RiverKind::River { .. }), _) => { + let (r, g, b) = ( + (column_rgb.r + * if is_temperature { + temperature as f64 + } else { + column_rgb.r + }) + .sqrt(), + column_rgb.g, + (column_rgb.b + * if is_humidity { + humidity as f64 + } else { + column_rgb.b + }) + .sqrt(), + ); + Rgb::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8) + }, + (None | Some(RiverKind::Lake { .. } | RiverKind::Ocean), _) => Rgb::new( + 0, + ((g_water - water_depth * g_water) * 1.0) as u8, + ((b_water - water_depth * b_water) * 1.0) as u8, + ), + }; + // TODO: Make principled. + let rgb = if near_site { + Rgb::new(0x57, 0x39, 0x33) + } else if is_path { + Rgb::new(0x37, 0x29, 0x23) + } else if is_cave { + Rgb::new(0x37, 0x37, 0x37) + } else { + rgb + }; + + MapSample { + rgb, + alt: if is_water { + true_alt.max(true_water_alt) + } else { + true_alt + }, + downhill_wpos, + connections: if has_connections { + Some(connections) + } else { + None + }, } } diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index d8a3239e23..7eff3d9d8a 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -14,33 +14,38 @@ pub use self::{ get_rivers, mrec_downhill, Alt, RiverData, RiverKind, }, location::Location, - map::{MapConfig, MapDebug}, + map::{sample_pos, sample_wpos}, util::{ - cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors, - uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, - NEIGHBOR_DELTA, + cdf_irwin_hall, downhill, get_horizon_map, get_oceans, local_cells, map_edge_factor, + uniform_noise, uphill, InverseCdf, ScaleBias, }, way::{Cave, Path, Way}, }; use crate::{ all::ForestKind, + block::BlockGen, civ::Place, + column::ColumnGen, site::Site, - util::{seed_expan, FastNoise, RandomField, StructureGen2d, LOCALITY, NEIGHBORS}, - Index, CONFIG, + util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d, LOCALITY, NEIGHBORS}, + IndexRef, CONFIG, }; use common::{ assets, + msg::server::WorldMapMsg, store::Id, - terrain::{BiomeKind, TerrainChunkSize}, + terrain::{ + map::MapConfig, uniform_idx_as_vec2, vec2_as_uniform_idx, BiomeKind, MapSizeLg, + TerrainChunkSize, + }, vol::RectVolSize, }; use noise::{ BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti, Seedable, SuperSimplex, Worley, }; -use num::{Float, Signed}; +use num::{traits::FloatConst, Float, Signed}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; use rayon::prelude::*; @@ -55,19 +60,17 @@ use std::{ use tracing::{debug, warn}; use vek::*; -// NOTE: I suspect this is too small (1024 * 16 * 1024 * 16 * 8 doesn't fit in -// an i32), but we'll see what happens, I guess! We could always store sizes >> -// 3. I think 32 or 64 is the absolute limit though, and would require -// substantial changes. Also, 1024 * 16 * 1024 * 16 is no longer -// cleanly representable in f32 (that stops around 1024 * 4 * 1024 * 4, for -// signed floats anyway) but I think that is probably less important since I -// don't think we actually cast a chunk id to float, just coordinates... could -// be wrong though! -#[allow(clippy::identity_op)] // TODO: Pending review in #587 -pub const WORLD_SIZE: Vec2 = Vec2 { - x: 1024 * 1, - y: 1024 * 1, -}; +/// Default base two logarithm of the world size, in chunks, per dimension. +/// +/// Currently, our default map dimensions are 2^10 × 2^10 chunks, +/// mostly for historical reasons. It is likely that we will increase this +/// default at some point. +const DEFAULT_WORLD_CHUNKS_LG: MapSizeLg = + if let Ok(map_size_lg) = MapSizeLg::new(Vec2 { x: 10, y: 10 }) { + map_size_lg + } else { + panic!("Default world chunk size does not satisfy required invariants."); + }; /// A structure that holds cached noise values and cumulative distribution /// functions for the input that led to those values. See the definition of @@ -182,6 +185,23 @@ pub struct WorldMap_0_5_0 { pub basement: Box<[Alt]>, } +/// Version of the world map intended for use in Veloren 0.7.0. +#[derive(Serialize, Deserialize)] +#[repr(C)] +pub struct WorldMap_0_7_0 { + /// Saved map size. + pub map_size_lg: Vec2, + /// Saved continent_scale hack, to try to better approximate the correct + /// seed according to varying map size. + /// + /// TODO: Remove when generating new maps becomes more principled. + pub continent_scale_hack: f64, + /// Saved altitude height map. + pub alt: Box<[Alt]>, + /// Saved basement height map. + pub basement: Box<[Alt]>, +} + /// Errors when converting a map to the most recent type (currently, /// shared by the various map types, but at some point we might switch to /// version-specific errors if it feels worthwhile). @@ -220,11 +240,12 @@ pub enum WorldFileError { #[repr(u32)] pub enum WorldFile { Veloren0_5_0(WorldMap_0_5_0) = 0, + Veloren0_7_0(WorldMap_0_7_0) = 1, } /// Data for the most recent map type. Update this when you add a new map /// verson. -pub type ModernMap = WorldMap_0_5_0; +pub type ModernMap = WorldMap_0_7_0; /// The default world map. /// @@ -245,9 +266,9 @@ impl WorldFileLegacy { /// should construct a call chain that ultimately ends up with a modern /// version. pub fn into_modern(self) -> Result { - if self.alt.len() != self.basement.len() - || self.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize - { + // NOTE: At this point, we ssume that any remaining legacy maps were 1024 × + // 1024. + if self.alt.len() != self.basement.len() || self.alt.len() != 1024 * 1024 { return Err(WorldFileError::WorldSizeInvalid); } @@ -261,10 +282,35 @@ impl WorldFileLegacy { } impl WorldMap_0_5_0 { + #[inline] + pub fn into_modern(self) -> Result { + let pow_size = (self.alt.len().trailing_zeros()) / 2; + let two_coord_size = 1 << (2 * pow_size); + if self.alt.len() != self.basement.len() || self.alt.len() != two_coord_size { + return Err(WorldFileError::WorldSizeInvalid); + } + + // The recommended continent scale for maps from version 0.5.0 is (in all + // existing cases) just 1.0 << (f64::from(pow_size) - 10.0). + let continent_scale_hack = (f64::from(pow_size) - 10.0).exp2(); + + let map = WorldMap_0_7_0 { + map_size_lg: Vec2::new(pow_size, pow_size), + continent_scale_hack, + alt: self.alt, + basement: self.basement, + }; + + map.into_modern() + } +} + +impl WorldMap_0_7_0 { #[inline] pub fn into_modern(self) -> Result { if self.alt.len() != self.basement.len() - || self.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize + || self.alt.len() != (1 << (self.map_size_lg.x + self.map_size_lg.y)) + || self.continent_scale_hack <= 0.0 { return Err(WorldFileError::WorldSizeInvalid); } @@ -279,7 +325,7 @@ impl WorldFile { /// variant we construct here to make sure we're using the latest map /// version. - pub fn new(map: ModernMap) -> Self { WorldFile::Veloren0_5_0(map) } + pub fn new(map: ModernMap) -> Self { WorldFile::Veloren0_7_0(map) } #[inline] /// Turns a WorldFile into the latest version. Whenever a new map version @@ -287,12 +333,15 @@ impl WorldFile { pub fn into_modern(self) -> Result { match self { WorldFile::Veloren0_5_0(map) => map.into_modern(), + WorldFile::Veloren0_7_0(map) => map.into_modern(), } } } pub struct WorldSim { pub seed: u32, + /// Base 2 logarithm of the map size. + map_size_lg: MapSizeLg, /// Maximum height above sea level of any chunk in the map (not including /// post-erosion warping, cliffs, and other things like that). pub max_height: f32, @@ -307,569 +356,6 @@ impl WorldSim { #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 pub fn generate(seed: u32, opts: WorldOpts) -> Self { - let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); - // NOTE: Change 1.0 to 4.0, while multiplying grid_size by 4, for a 4x - // improvement in world detail. You may also want to set mins_per_sec to 1 / - // (4.0 * 4.0) in ./erosion.rs, in order to get a similar rate of river - // formation. - let continent_scale = 1.0/*4.0*/ - * 5_000.0f64 - .div(32.0) - .mul(TerrainChunkSize::RECT_SIZE.x as f64); - let rock_lacunarity = 2.0; - let uplift_scale = 128.0; - let uplift_turb_scale = uplift_scale / 4.0; - - // NOTE: Changing order will significantly change WorldGen, so try not to! - let gen_ctx = GenCtx { - turb_x_nz: SuperSimplex::new().set_seed(rng.gen()), - turb_y_nz: SuperSimplex::new().set_seed(rng.gen()), - chaos_nz: RidgedMulti::new() - .set_octaves(7) - .set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale)) - .set_seed(rng.gen()), - hill_nz: SuperSimplex::new().set_seed(rng.gen()), - alt_nz: util::HybridMulti::new() - .set_octaves(8) - .set_frequency((10_000.0 / continent_scale) as f64) - // persistence = lacunarity^(-(1.0 - fractal increment)) - .set_lacunarity(util::HybridMulti::DEFAULT_LACUNARITY) - .set_persistence(util::HybridMulti::DEFAULT_LACUNARITY.powf(-(1.0 - 0.0))) - .set_offset(0.0) - .set_seed(rng.gen()), - temp_nz: Fbm::new() - .set_octaves(6) - .set_persistence(0.5) - .set_frequency(1.0 / (((1 << 6) * 64) as f64)) - .set_lacunarity(2.0) - .set_seed(rng.gen()), - - small_nz: BasicMulti::new().set_octaves(2).set_seed(rng.gen()), - rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), - cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), - warp_nz: FastNoise::new(rng.gen()), - tree_nz: BasicMulti::new() - .set_octaves(12) - .set_persistence(0.75) - .set_seed(rng.gen()), - cave_0_nz: SuperSimplex::new().set_seed(rng.gen()), - cave_1_nz: SuperSimplex::new().set_seed(rng.gen()), - - structure_gen: StructureGen2d::new(rng.gen(), 32, 16), - region_gen: StructureGen2d::new(rng.gen(), 400, 96), - cliff_gen: StructureGen2d::new(rng.gen(), 80, 56), - humid_nz: Billow::new() - .set_octaves(9) - .set_persistence(0.4) - .set_frequency(0.2) - .set_seed(rng.gen()), - - fast_turb_x_nz: FastNoise::new(rng.gen()), - fast_turb_y_nz: FastNoise::new(rng.gen()), - - town_gen: StructureGen2d::new(rng.gen(), 2048, 1024), - river_seed: RandomField::new(rng.gen()), - rock_strength_nz: Fbm::new() - .set_octaves(10) - .set_lacunarity(rock_lacunarity) - // persistence = lacunarity^(-(1.0 - fractal increment)) - // NOTE: In paper, fractal increment is roughly 0.25. - .set_persistence(rock_lacunarity.powf(-(1.0 - 0.25))) - .set_frequency( - 1.0 * (5_000.0 / continent_scale) - / (2.0 * TerrainChunkSize::RECT_SIZE.x as f64 * 2.0.powi(10 - 1)), - ) - .set_seed(rng.gen()), - uplift_nz: Worley::new() - .set_seed(rng.gen()) - .set_frequency(1.0 / (TerrainChunkSize::RECT_SIZE.x as f64 * uplift_scale)) - .set_displacement(1.0) - .set_range_function(RangeFunction::Euclidean), - }; - - let river_seed = &gen_ctx.river_seed; - let rock_strength_nz = &gen_ctx.rock_strength_nz; - - // Suppose the old world has grid spacing Δx' = Δy', new Δx = Δy. - // We define grid_scale such that Δx = height_scale * Δx' ⇒ - // grid_scale = Δx / Δx'. - let grid_scale = 1.0f64 / 4.0/*1.0*/; - - // Now, suppose we want to generate a world with "similar" topography, defined - // in this case as having roughly equal slopes at steady state, with the - // simulation taking roughly as many steps to get to the point the - // previous world was at when it finished being simulated. - // - // Some computations with our coupled SPL/debris flow give us (for slope S - // constant) the following suggested scaling parameters to make this - // work: k_fs_scale ≡ (K𝑓 / K𝑓') = grid_scale^(-2m) = - // grid_scale^(-2θn) - let k_fs_scale = |theta, n| grid_scale.powf(-2.0 * (theta * n) as f64); - - // k_da_scale ≡ (K_da / K_da') = grid_scale^(-2q) - let k_da_scale = |q| grid_scale.powf(-2.0 * q); - // - // Some other estimated parameters are harder to come by and *much* more - // dubious, not being accurate for the coupled equation. But for the SPL - // only one we roughly find, for h the height at steady state and time τ - // = time to steady state, with Hack's Law estimated b = 2.0 and various other - // simplifying assumptions, the estimate: - // height_scale ≡ (h / h') = grid_scale^(n) - let height_scale = |n: f32| grid_scale.powf(n as f64) as Alt; - // time_scale ≡ (τ / τ') = grid_scale^(n) - let time_scale = |n: f32| grid_scale.powf(n as f64); - // - // Based on this estimate, we have: - // delta_t_scale ≡ (Δt / Δt') = time_scale - let delta_t_scale = |n: f32| time_scale(n); - // alpha_scale ≡ (α / α') = height_scale^(-1) - let alpha_scale = |n: f32| height_scale(n).recip() as f32; - // - // Slightly more dubiously (need to work out the math better) we find: - // k_d_scale ≡ (K_d / K_d') = grid_scale^2 / (/*height_scale * */ time_scale) - let k_d_scale = |n: f32| grid_scale.powi(2) / (/* height_scale(n) * */time_scale(n)); - // epsilon_0_scale ≡ (ε₀ / ε₀') = height_scale(n) / time_scale(n) - let epsilon_0_scale = |n| (height_scale(n) / time_scale(n) as Alt) as f32; - - // Approximate n for purposes of computation of parameters above over the whole - // grid (when a chunk isn't available). - let n_approx = 1.0; - let max_erosion_per_delta_t = 64.0 * delta_t_scale(n_approx); - let n_steps = 100; - let n_small_steps = 0; - let n_post_load_steps = 0; - - // Logistic regression. Make sure x ∈ (0, 1). - let logit = |x: f64| x.ln() - (-x).ln_1p(); - // 0.5 + 0.5 * tanh(ln(1 / (1 - 0.1) - 1) / (2 * (sqrt(3)/pi))) - let logistic_2_base = 3.0f64.sqrt() * f64::consts::FRAC_2_PI; - // Assumes μ = 0, σ = 1 - let logistic_cdf = |x: f64| (x / logistic_2_base).tanh() * 0.5 + 0.5; - - let min_epsilon = - 1.0 / (WORLD_SIZE.x as f64 * WORLD_SIZE.y as f64).max(f64::EPSILON as f64 * 0.5); - let max_epsilon = (1.0 - 1.0 / (WORLD_SIZE.x as f64 * WORLD_SIZE.y as f64)) - .min(1.0 - f64::EPSILON as f64 * 0.5); - - // No NaNs in these uniform vectors, since the original noise value always - // returns Some. - let ((alt_base, _), (chaos, _)) = rayon::join( - || { - uniform_noise(|_, wposf| { - // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied - // value is from -0.35 * (CONFIG.mountain_scale * 1.05) to - // 0.35 * (CONFIG.mountain_scale * 0.95), but value here is from -0.3675 to - // 0.3325). - Some( - (gen_ctx - .alt_nz - .get((wposf.div(10_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .sub(0.05) - .mul(0.35), - ) - }) - }, - || { - uniform_noise(|_, wposf| { - // From 0 to 1.6, but the distribution before the max is from -1 and 1.6, so - // there is a 50% chance that hill will end up at 0.3 or - // lower, and probably a very high change it will be exactly - // 0. - let hill = (0.0f64 - + gen_ctx - .hill_nz - .get( - (wposf - .mul(32.0) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(1_500.0)) - .into_array(), - ) - .min(1.0) - .max(-1.0) - .mul(1.0) - + gen_ctx - .hill_nz - .get( - (wposf - .mul(32.0) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(400.0)) - .into_array(), - ) - .min(1.0) - .max(-1.0) - .mul(0.3)) - .add(0.3) - .max(0.0); - - // chaos produces a value in [0.12, 1.32]. It is a meta-level factor intended - // to reflect how "chaotic" the region is--how much weird - // stuff is going on on this terrain. - Some( - ((gen_ctx - .chaos_nz - .get((wposf.div(3_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .add(1.0) - .mul(0.5) - // [0, 1] * [0.4, 1] = [0, 1] (but probably towards the lower end) - .mul( - (gen_ctx - .chaos_nz - .get((wposf.div(6_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .abs() - .max(0.4) - .min(1.0), - ) - // Chaos is always increased by a little when we're on a hill (but remember - // that hill is 0.3 or less about 50% of the time). - // [0, 1] + 0.2 * [0, 1.6] = [0, 1.32] - .add(0.2 * hill) - // We can't have *no* chaos! - .max(0.12)) as f32, - ) - }) - }, - ); - - // We ignore sea level because we actually want to be relative to sea level here - // and want things in CONFIG.mountain_scale units, but otherwise this is - // a correct altitude calculation. Note that this is using the - // "unadjusted" temperature. - // - // No NaNs in these uniform vectors, since the original noise value always - // returns Some. - let (alt_old, _) = uniform_noise(|posi, wposf| { - // This is the extension upwards from the base added to some extra noise from -1 - // to 1. - // - // The extra noise is multiplied by alt_main (the mountain part of the - // extension) powered to 0.8 and clamped to [0.15, 1], to get a - // value between [-1, 1] again. - // - // The sides then receive the sequence (y * 0.3 + 1.0) * 0.4, so we have - // [-1*1*(1*0.3+1)*0.4, 1*(1*0.3+1)*0.4] = [-0.52, 0.52]. - // - // Adding this to alt_main thus yields a value between -0.4 (if alt_main = 0 and - // gen_ctx = -1, 0+-1*(0*.3+1)*0.4) and 1.52 (if alt_main = 1 and gen_ctx = 1). - // Most of the points are above 0. - // - // Next, we add again by a sin of alt_main (between [-1, 1])^pow, getting - // us (after adjusting for sign) another value between [-1, 1], and then this is - // multiplied by 0.045 to get [-0.045, 0.045], which is added to [-0.4, 0.52] to - // get [-0.445, 0.565]. - let alt_main = { - // Extension upwards from the base. A positive number from 0 to 1 curved to be - // maximal at 0. Also to be multiplied by CONFIG.mountain_scale. - let alt_main = (gen_ctx - .alt_nz - .get((wposf.div(2_000.0)).into_array()) - .min(1.0) - .max(-1.0)) - .abs() - .powf(1.35); - - fn spring(x: f64, pow: f64) -> f64 { x.abs().powf(pow) * x.signum() } - - 0.0 + alt_main - + (gen_ctx - .small_nz - .get( - (wposf - .mul(32.0) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(300.0)) - .into_array(), - ) - .min(1.0) - .max(-1.0)) - .mul(alt_main.powf(0.8).max(/* 0.25 */ 0.15)) - .mul(0.3) - .add(1.0) - .mul(0.4) - + spring(alt_main.abs().powf(0.5).min(0.75).mul(60.0).sin(), 4.0).mul(0.045) - }; - - // Now we can compute the final altitude using chaos. - // We multiply by chaos clamped to [0.1, 1.32] to get a value between [0.03, - // 2.232] for alt_pre, then multiply by CONFIG.mountain_scale and - // add to the base and sea level to get an adjusted value, then - // multiply the whole thing by map_edge_factor (TODO: compute final - // bounds). - // - // [-.3675, .3325] + [-0.445, 0.565] * [0.12, 1.32]^1.2 - // ~ [-.3675, .3325] + [-0.445, 0.565] * [0.07, 1.40] - // = [-.3675, .3325] + ([-0.5785, 0.7345]) - // = [-0.946, 1.067] - Some( - ((alt_base[posi].1 + alt_main.mul((chaos[posi].1 as f64).powf(1.2))) - .mul(map_edge_factor(posi) as f64) - .add( - (CONFIG.sea_level as f64) - .div(CONFIG.mountain_scale as f64) - .mul(map_edge_factor(posi) as f64), - ) - .sub((CONFIG.sea_level as f64).div(CONFIG.mountain_scale as f64))) - as f32, - ) - }); - - // Calculate oceans. - let is_ocean = get_oceans(|posi: usize| alt_old[posi].1); - // NOTE: Uncomment if you want oceans to exclusively be on the border of the - // map. - /* let is_ocean = (0..WORLD_SIZE.x * WORLD_SIZE.y) - .into_par_iter() - .map(|i| map_edge_factor(i) == 0.0) - .collect::>(); */ - let is_ocean_fn = |posi: usize| is_ocean[posi]; - - let turb_wposf_div = 8.0; - let n_func = |posi| { - if is_ocean_fn(posi) { - return 1.0; - } - 1.0 - }; - let old_height = |posi: usize| { - alt_old[posi].1 * CONFIG.mountain_scale * height_scale(n_func(posi)) as f32 - }; - - // NOTE: Needed if you wish to use the distance to the point defining the Worley - // cell, not just the value within that cell. - // let uplift_nz_dist = gen_ctx.uplift_nz.clone().enable_range(true); - - // Recalculate altitudes without oceans. - // NaNs in these uniform vectors wherever is_ocean_fn returns true. - let (alt_old_no_ocean, _) = uniform_noise(|posi, _| { - if is_ocean_fn(posi) { - None - } else { - Some(old_height(posi)) - } - }); - let (uplift_uniform, _) = uniform_noise(|posi, _wposf| { - if is_ocean_fn(posi) { - None - } else { - let oheight = alt_old_no_ocean[posi].0 as f64 - 0.5; - let height = (oheight + 0.5).powf(2.0); - Some(height) - } - }); - - let alt_old_min_uniform = 0.0; - let alt_old_max_uniform = 1.0; - - let inv_func = |x: f64| x; - let alt_exp_min_uniform = inv_func(min_epsilon); - let alt_exp_max_uniform = inv_func(max_epsilon); - - let erosion_factor = |x: f64| { - (inv_func(x) - alt_exp_min_uniform) / (alt_exp_max_uniform - alt_exp_min_uniform) - }; - let rock_strength_div_factor = (2.0 * TerrainChunkSize::RECT_SIZE.x as f64) / 8.0; - let theta_func = |_posi| 0.4; - let kf_func = { - |posi| { - let kf_scale_i = k_fs_scale(theta_func(posi), n_func(posi)) as f64; - if is_ocean_fn(posi) { - return 1.0e-4 * kf_scale_i; - } - - let kf_i = // kf = 1.5e-4: high-high (plateau [fan sediment]) - // kf = 1e-4: high (plateau) - // kf = 2e-5: normal (dike [unexposed]) - // kf = 1e-6: normal-low (dike [exposed]) - // kf = 2e-6: low (mountain) - // -- - // kf = 2.5e-7 to 8e-7: very low (Cordonnier papers on plate tectonics) - // ((1.0 - uheight) * (1.5e-4 - 2.0e-6) + 2.0e-6) as f32 - // - // ACTUAL recorded values worldwide: much lower... - 1.0e-6 - ; - kf_i * kf_scale_i - } - }; - let kd_func = { - |posi| { - let n = n_func(posi); - let kd_scale_i = k_d_scale(n); - if is_ocean_fn(posi) { - let kd_i = 1.0e-2 / 4.0; - return kd_i * kd_scale_i; - } - // kd = 1e-1: high (mountain, dike) - // kd = 1.5e-2: normal-high (plateau [fan sediment]) - // kd = 1e-2: normal (plateau) - let kd_i = 1.0e-2 / 4.0; - kd_i * kd_scale_i - } - }; - let g_func = |posi| { - if map_edge_factor(posi) == 0.0 { - return 0.0; - } - // G = d* v_s / p_0, where - // v_s is the settling velocity of sediment grains - // p_0 is the mean precipitation rate - // d* is the sediment concentration ratio (between concentration near riverbed - // interface, and average concentration over the water column). - // d* varies with Rouse number which defines relative contribution of bed, - // suspended, and washed loads. - // - // G is typically on the order of 1 or greater. However, we are only guaranteed - // to converge for G ≤ 1, so we keep it in the chaos range of [0.12, - // 1.32]. - 1.0 - }; - let epsilon_0_func = |posi| { - // epsilon_0_scale is roughly [using Hack's Law with b = 2 and SPL without - // debris flow or hillslopes] equal to the ratio of the old to new - // area, to the power of -n_i. - let epsilon_0_scale_i = epsilon_0_scale(n_func(posi)); - if is_ocean_fn(posi) { - // marine: ε₀ = 2.078e-3 - let epsilon_0_i = 2.078e-3 / 4.0; - return epsilon_0_i * epsilon_0_scale_i; - } - let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - .map(|e| e as f64); - let turb_wposf = wposf - .mul(5_000.0 / continent_scale) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(turb_wposf_div); - let turb = Vec2::new( - gen_ctx.turb_x_nz.get(turb_wposf.into_array()), - gen_ctx.turb_y_nz.get(turb_wposf.into_array()), - ) * uplift_turb_scale - * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); - let turb_wposf = wposf + turb; - let uheight = gen_ctx - .uplift_nz - .get(turb_wposf.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let wposf3 = Vec3::new( - wposf.x, - wposf.y, - uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, - ); - let rock_strength = gen_ctx - .rock_strength_nz - .get(wposf3.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let center = 0.4; - let dmin = center - 0.05; - let dmax = center + 0.05; - let log_odds = |x: f64| logit(x) - logit(center); - let ustrength = logistic_cdf( - 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) - + 1.0 * log_odds(uheight.min(dmax).max(dmin)), - ); - // marine: ε₀ = 2.078e-3 - // San Gabriel Mountains: ε₀ = 3.18e-4 - // Oregon Coast Range: ε₀ = 2.68e-4 - // Frogs Hollow (peak production = 0.25): ε₀ = 1.41e-4 - // Point Reyes: ε₀ = 8.1e-5 - // Nunnock River (fractured granite, least weathered?): ε₀ = 5.3e-5 - let epsilon_0_i = ((1.0 - ustrength) * (2.078e-3 - 5.3e-5) + 5.3e-5) as f32 / 4.0; - epsilon_0_i * epsilon_0_scale_i - }; - let alpha_func = |posi| { - let alpha_scale_i = alpha_scale(n_func(posi)); - if is_ocean_fn(posi) { - // marine: α = 3.7e-2 - return 3.7e-2 * alpha_scale_i; - } - let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - .map(|e| e as f64); - let turb_wposf = wposf - .mul(5_000.0 / continent_scale) - .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) - .div(turb_wposf_div); - let turb = Vec2::new( - gen_ctx.turb_x_nz.get(turb_wposf.into_array()), - gen_ctx.turb_y_nz.get(turb_wposf.into_array()), - ) * uplift_turb_scale - * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); - let turb_wposf = wposf + turb; - let uheight = gen_ctx - .uplift_nz - .get(turb_wposf.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let wposf3 = Vec3::new( - wposf.x, - wposf.y, - uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, - ); - let rock_strength = gen_ctx - .rock_strength_nz - .get(wposf3.into_array()) - .min(1.0) - .max(-1.0) - .mul(0.5) - .add(0.5); - let center = 0.4; - let dmin = center - 0.05; - let dmax = center + 0.05; - let log_odds = |x: f64| logit(x) - logit(center); - let ustrength = logistic_cdf( - 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) - + 1.0 * log_odds(uheight.min(dmax).max(dmin)), - ); - // Frog Hollow (peak production = 0.25): α = 4.2e-2 - // San Gabriel Mountains: α = 3.8e-2 - // marine: α = 3.7e-2 - // Oregon Coast Range: α = 3e-2 - // Nunnock river (fractured granite, least weathered?): α = 2e-3 - // Point Reyes: α = 1.6e-2 - // The stronger the rock, the faster the decline in soil production. - let alpha_i = (ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32; - alpha_i * alpha_scale_i - }; - let uplift_fn = |posi| { - if is_ocean_fn(posi) { - return 0.0; - } - let height = (uplift_uniform[posi].1 - alt_old_min_uniform) as f64 - / (alt_old_max_uniform - alt_old_min_uniform) as f64; - - let height = height.mul(max_epsilon - min_epsilon).add(min_epsilon); - let height = erosion_factor(height); - assert!(height >= 0.0); - assert!(height <= 1.0); - - // u = 1e-3: normal-high (dike, mountain) - // u = 5e-4: normal (mid example in Yuan, average mountain uplift) - // u = 2e-4: low (low example in Yuan; known that lagoons etc. may have u ~ - // 0.05). u = 0: low (plateau [fan, altitude = 0.0]) - let height = height.mul(max_erosion_per_delta_t); - height as f64 - }; - let alt_func = |posi| { - if is_ocean_fn(posi) { - old_height(posi) - } else { - (old_height(posi) as f64 / CONFIG.mountain_scale as f64) as f32 - 0.5 - } - }; - // Parse out the contents of various map formats into the values we need. let parsed_world_file = (|| { let map = match opts.world_file { @@ -958,12 +444,598 @@ impl WorldSim { } })(); + // NOTE: Change 1.0 to 4.0 for a 4x + // improvement in world detail. We also use this to automatically adjust + // grid_scale (multiplying by 4.0) and multiply mins_per_sec by + // 1.0 / (4.0 * 4.0) in ./erosion.rs, in order to get a similar rate of river + // formation. + // + // FIXME: This is a hack! At some point we will hae a more principled way of + // dealing with this. + let continent_scale_hack = 1.0/*4.0*/; + let (parsed_world_file, map_size_lg) = parsed_world_file + .and_then(|map| match MapSizeLg::new(map.map_size_lg) { + Ok(map_size_lg) => Some((Some(map), map_size_lg)), + Err(e) => { + warn!("World size of map does not satisfy invariants: {:?}", e); + None + }, + }) + .unwrap_or((None, DEFAULT_WORLD_CHUNKS_LG)); + let continent_scale_hack = if let Some(map) = &parsed_world_file { + map.continent_scale_hack + } else { + continent_scale_hack + }; + + let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + let continent_scale = continent_scale_hack + * 5_000.0f64 + .div(32.0) + .mul(TerrainChunkSize::RECT_SIZE.x as f64); + let rock_lacunarity = 2.0; + let uplift_scale = 128.0; + let uplift_turb_scale = uplift_scale / 4.0; + + // NOTE: Changing order will significantly change WorldGen, so try not to! + let gen_ctx = GenCtx { + turb_x_nz: SuperSimplex::new().set_seed(rng.gen()), + turb_y_nz: SuperSimplex::new().set_seed(rng.gen()), + chaos_nz: RidgedMulti::new() + .set_octaves(7) + .set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale)) + .set_seed(rng.gen()), + hill_nz: SuperSimplex::new().set_seed(rng.gen()), + alt_nz: util::HybridMulti::new() + .set_octaves(8) + .set_frequency((10_000.0 / continent_scale) as f64) + // persistence = lacunarity^(-(1.0 - fractal increment)) + .set_lacunarity(util::HybridMulti::DEFAULT_LACUNARITY) + .set_persistence(util::HybridMulti::DEFAULT_LACUNARITY.powf(-(1.0 - 0.0))) + .set_offset(0.0) + .set_seed(rng.gen()), + temp_nz: Fbm::new() + .set_octaves(6) + .set_persistence(0.5) + .set_frequency(1.0 / (((1 << 6) * 64) as f64)) + .set_lacunarity(2.0) + .set_seed(rng.gen()), + + small_nz: BasicMulti::new().set_octaves(2).set_seed(rng.gen()), + rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), + cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(rng.gen()), + warp_nz: FastNoise::new(rng.gen()), + tree_nz: BasicMulti::new() + .set_octaves(12) + .set_persistence(0.75) + .set_seed(rng.gen()), + cave_0_nz: SuperSimplex::new().set_seed(rng.gen()), + cave_1_nz: SuperSimplex::new().set_seed(rng.gen()), + + structure_gen: StructureGen2d::new(rng.gen(), 32, 16), + region_gen: StructureGen2d::new(rng.gen(), 400, 96), + cliff_gen: StructureGen2d::new(rng.gen(), 80, 56), + humid_nz: Billow::new() + .set_octaves(9) + .set_persistence(0.4) + .set_frequency(0.2) + .set_seed(rng.gen()), + + fast_turb_x_nz: FastNoise::new(rng.gen()), + fast_turb_y_nz: FastNoise::new(rng.gen()), + + town_gen: StructureGen2d::new(rng.gen(), 2048, 1024), + river_seed: RandomField::new(rng.gen()), + rock_strength_nz: Fbm::new() + .set_octaves(10) + .set_lacunarity(rock_lacunarity) + // persistence = lacunarity^(-(1.0 - fractal increment)) + // NOTE: In paper, fractal increment is roughly 0.25. + .set_persistence(rock_lacunarity.powf(-(1.0 - 0.25))) + .set_frequency( + 1.0 * (5_000.0 / continent_scale) + / (2.0 * TerrainChunkSize::RECT_SIZE.x as f64 * 2.0.powi(10 - 1)), + ) + .set_seed(rng.gen()), + uplift_nz: Worley::new() + .set_seed(rng.gen()) + .set_frequency(1.0 / (TerrainChunkSize::RECT_SIZE.x as f64 * uplift_scale)) + .set_displacement(1.0) + .set_range_function(RangeFunction::Euclidean), + }; + + let river_seed = &gen_ctx.river_seed; + let rock_strength_nz = &gen_ctx.rock_strength_nz; + + // Suppose the old world has grid spacing Δx' = Δy', new Δx = Δy. + // We define grid_scale such that Δx = height_scale * Δx' ⇒ + // grid_scale = Δx / Δx'. + let grid_scale = 1.0f64 / (4.0 / continent_scale_hack)/*1.0*/; + + // Now, suppose we want to generate a world with "similar" topography, defined + // in this case as having roughly equal slopes at steady state, with the + // simulation taking roughly as many steps to get to the point the + // previous world was at when it finished being simulated. + // + // Some computations with our coupled SPL/debris flow give us (for slope S + // constant) the following suggested scaling parameters to make this + // work: k_fs_scale ≡ (K𝑓 / K𝑓') = grid_scale^(-2m) = + // grid_scale^(-2θn) + let k_fs_scale = |theta, n| grid_scale.powf(-2.0 * (theta * n) as f64); + + // k_da_scale ≡ (K_da / K_da') = grid_scale^(-2q) + let k_da_scale = |q| grid_scale.powf(-2.0 * q); + // + // Some other estimated parameters are harder to come by and *much* more + // dubious, not being accurate for the coupled equation. But for the SPL + // only one we roughly find, for h the height at steady state and time τ + // = time to steady state, with Hack's Law estimated b = 2.0 and various other + // simplifying assumptions, the estimate: + // height_scale ≡ (h / h') = grid_scale^(n) + let height_scale = |n: f32| grid_scale.powf(n as f64) as Alt; + // time_scale ≡ (τ / τ') = grid_scale^(n) + let time_scale = |n: f32| grid_scale.powf(n as f64); + // + // Based on this estimate, we have: + // delta_t_scale ≡ (Δt / Δt') = time_scale + let delta_t_scale = |n: f32| time_scale(n); + // alpha_scale ≡ (α / α') = height_scale^(-1) + let alpha_scale = |n: f32| height_scale(n).recip() as f32; + // + // Slightly more dubiously (need to work out the math better) we find: + // k_d_scale ≡ (K_d / K_d') = grid_scale^2 / (/*height_scale * */ time_scale) + let k_d_scale = |n: f32| grid_scale.powi(2) / (/* height_scale(n) * */time_scale(n)); + // epsilon_0_scale ≡ (ε₀ / ε₀') = height_scale(n) / time_scale(n) + let epsilon_0_scale = |n| (height_scale(n) / time_scale(n) as Alt) as f32; + + // Approximate n for purposes of computation of parameters above over the whole + // grid (when a chunk isn't available). + let n_approx = 1.0; + let max_erosion_per_delta_t = 64.0 * delta_t_scale(n_approx); + let n_steps = 100; + let n_small_steps = 0; + let n_post_load_steps = 0; + + // Logistic regression. Make sure x ∈ (0, 1). + let logit = |x: f64| x.ln() - (-x).ln_1p(); + // 0.5 + 0.5 * tanh(ln(1 / (1 - 0.1) - 1) / (2 * (sqrt(3)/pi))) + let logistic_2_base = 3.0f64.sqrt() * f64::consts::FRAC_2_PI; + // Assumes μ = 0, σ = 1 + let logistic_cdf = |x: f64| (x / logistic_2_base).tanh() * 0.5 + 0.5; + + let map_size_chunks_len_f64 = map_size_lg.chunks().map(f64::from).product(); + let min_epsilon = 1.0 / map_size_chunks_len_f64.max(f64::EPSILON as f64 * 0.5); + let max_epsilon = + (1.0 - 1.0 / map_size_chunks_len_f64).min(1.0 - f64::EPSILON as f64 * 0.5); + + // No NaNs in these uniform vectors, since the original noise value always + // returns Some. + let ((alt_base, _), (chaos, _)) = rayon::join( + || { + uniform_noise(map_size_lg, |_, wposf| { + // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied + // value is from -0.35 * (CONFIG.mountain_scale * 1.05) to + // 0.35 * (CONFIG.mountain_scale * 0.95), but value here is from -0.3675 to + // 0.3325). + Some( + (gen_ctx + .alt_nz + .get((wposf.div(10_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .sub(0.05) + .mul(0.35), + ) + }) + }, + || { + uniform_noise(map_size_lg, |_, wposf| { + // From 0 to 1.6, but the distribution before the max is from -1 and 1.6, so + // there is a 50% chance that hill will end up at 0.3 or + // lower, and probably a very high change it will be exactly + // 0. + let hill = (0.0f64 + + gen_ctx + .hill_nz + .get( + (wposf + .mul(32.0) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(1_500.0)) + .into_array(), + ) + .min(1.0) + .max(-1.0) + .mul(1.0) + + gen_ctx + .hill_nz + .get( + (wposf + .mul(32.0) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(400.0)) + .into_array(), + ) + .min(1.0) + .max(-1.0) + .mul(0.3)) + .add(0.3) + .max(0.0); + + // chaos produces a value in [0.12, 1.32]. It is a meta-level factor intended + // to reflect how "chaotic" the region is--how much weird + // stuff is going on on this terrain. + Some( + ((gen_ctx + .chaos_nz + .get((wposf.div(3_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .add(1.0) + .mul(0.5) + // [0, 1] * [0.4, 1] = [0, 1] (but probably towards the lower end) + .mul( + (gen_ctx + .chaos_nz + .get((wposf.div(6_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .abs() + .max(0.4) + .min(1.0), + ) + // Chaos is always increased by a little when we're on a hill (but remember + // that hill is 0.3 or less about 50% of the time). + // [0, 1] + 0.2 * [0, 1.6] = [0, 1.32] + .add(0.2 * hill) + // We can't have *no* chaos! + .max(0.12)) as f32, + ) + }) + }, + ); + + // We ignore sea level because we actually want to be relative to sea level here + // and want things in CONFIG.mountain_scale units, but otherwise this is + // a correct altitude calculation. Note that this is using the + // "unadjusted" temperature. + // + // No NaNs in these uniform vectors, since the original noise value always + // returns Some. + let (alt_old, _) = uniform_noise(map_size_lg, |posi, wposf| { + // This is the extension upwards from the base added to some extra noise from -1 + // to 1. + // + // The extra noise is multiplied by alt_main (the mountain part of the + // extension) powered to 0.8 and clamped to [0.15, 1], to get a + // value between [-1, 1] again. + // + // The sides then receive the sequence (y * 0.3 + 1.0) * 0.4, so we have + // [-1*1*(1*0.3+1)*0.4, 1*(1*0.3+1)*0.4] = [-0.52, 0.52]. + // + // Adding this to alt_main thus yields a value between -0.4 (if alt_main = 0 and + // gen_ctx = -1, 0+-1*(0*.3+1)*0.4) and 1.52 (if alt_main = 1 and gen_ctx = 1). + // Most of the points are above 0. + // + // Next, we add again by a sin of alt_main (between [-1, 1])^pow, getting + // us (after adjusting for sign) another value between [-1, 1], and then this is + // multiplied by 0.045 to get [-0.045, 0.045], which is added to [-0.4, 0.52] to + // get [-0.445, 0.565]. + let alt_main = { + // Extension upwards from the base. A positive number from 0 to 1 curved to be + // maximal at 0. Also to be multiplied by CONFIG.mountain_scale. + let alt_main = (gen_ctx + .alt_nz + .get((wposf.div(2_000.0)).into_array()) + .min(1.0) + .max(-1.0)) + .abs() + .powf(1.35); + + fn spring(x: f64, pow: f64) -> f64 { x.abs().powf(pow) * x.signum() } + + 0.0 + alt_main + + (gen_ctx + .small_nz + .get( + (wposf + .mul(32.0) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(300.0)) + .into_array(), + ) + .min(1.0) + .max(-1.0)) + .mul(alt_main.powf(0.8).max(/* 0.25 */ 0.15)) + .mul(0.3) + .add(1.0) + .mul(0.4) + + spring(alt_main.abs().powf(0.5).min(0.75).mul(60.0).sin(), 4.0).mul(0.045) + }; + + // Now we can compute the final altitude using chaos. + // We multiply by chaos clamped to [0.1, 1.32] to get a value between [0.03, + // 2.232] for alt_pre, then multiply by CONFIG.mountain_scale and + // add to the base and sea level to get an adjusted value, then + // multiply the whole thing by map_edge_factor (TODO: compute final + // bounds). + // + // [-.3675, .3325] + [-0.445, 0.565] * [0.12, 1.32]^1.2 + // ~ [-.3675, .3325] + [-0.445, 0.565] * [0.07, 1.40] + // = [-.3675, .3325] + ([-0.5785, 0.7345]) + // = [-0.946, 1.067] + Some( + ((alt_base[posi].1 + alt_main.mul((chaos[posi].1 as f64).powf(1.2))) + .mul(map_edge_factor(map_size_lg, posi) as f64) + .add( + (CONFIG.sea_level as f64) + .div(CONFIG.mountain_scale as f64) + .mul(map_edge_factor(map_size_lg, posi) as f64), + ) + .sub((CONFIG.sea_level as f64).div(CONFIG.mountain_scale as f64))) + as f32, + ) + }); + + // Calculate oceans. + let is_ocean = get_oceans(map_size_lg, |posi: usize| alt_old[posi].1); + // NOTE: Uncomment if you want oceans to exclusively be on the border of the + // map. + /* let is_ocean = (0..map_size_lg.chunks()) + .into_par_iter() + .map(|i| map_edge_factor(map_size_lg, i) == 0.0) + .collect::>(); */ + let is_ocean_fn = |posi: usize| is_ocean[posi]; + + let turb_wposf_div = 8.0; + let n_func = |posi| { + if is_ocean_fn(posi) { + return 1.0; + } + 1.0 + }; + let old_height = |posi: usize| { + alt_old[posi].1 * CONFIG.mountain_scale * height_scale(n_func(posi)) as f32 + }; + + // NOTE: Needed if you wish to use the distance to the point defining the Worley + // cell, not just the value within that cell. + // let uplift_nz_dist = gen_ctx.uplift_nz.clone().enable_range(true); + + // Recalculate altitudes without oceans. + // NaNs in these uniform vectors wherever is_ocean_fn returns true. + let (alt_old_no_ocean, _) = uniform_noise(map_size_lg, |posi, _| { + if is_ocean_fn(posi) { + None + } else { + Some(old_height(posi)) + } + }); + let (uplift_uniform, _) = uniform_noise(map_size_lg, |posi, _wposf| { + if is_ocean_fn(posi) { + None + } else { + let oheight = alt_old_no_ocean[posi].0 as f64 - 0.5; + let height = (oheight + 0.5).powf(2.0); + Some(height) + } + }); + + let alt_old_min_uniform = 0.0; + let alt_old_max_uniform = 1.0; + + let inv_func = |x: f64| x; + let alt_exp_min_uniform = inv_func(min_epsilon); + let alt_exp_max_uniform = inv_func(max_epsilon); + + let erosion_factor = |x: f64| { + (inv_func(x) - alt_exp_min_uniform) / (alt_exp_max_uniform - alt_exp_min_uniform) + }; + let rock_strength_div_factor = (2.0 * TerrainChunkSize::RECT_SIZE.x as f64) / 8.0; + let theta_func = |_posi| 0.4; + let kf_func = { + |posi| { + let kf_scale_i = k_fs_scale(theta_func(posi), n_func(posi)) as f64; + if is_ocean_fn(posi) { + return 1.0e-4 * kf_scale_i; + } + + let kf_i = // kf = 1.5e-4: high-high (plateau [fan sediment]) + // kf = 1e-4: high (plateau) + // kf = 2e-5: normal (dike [unexposed]) + // kf = 1e-6: normal-low (dike [exposed]) + // kf = 2e-6: low (mountain) + // -- + // kf = 2.5e-7 to 8e-7: very low (Cordonnier papers on plate tectonics) + // ((1.0 - uheight) * (1.5e-4 - 2.0e-6) + 2.0e-6) as f32 + // + // ACTUAL recorded values worldwide: much lower... + 1.0e-6 + ; + kf_i * kf_scale_i + } + }; + let kd_func = { + |posi| { + let n = n_func(posi); + let kd_scale_i = k_d_scale(n); + if is_ocean_fn(posi) { + let kd_i = 1.0e-2 / 4.0; + return kd_i * kd_scale_i; + } + // kd = 1e-1: high (mountain, dike) + // kd = 1.5e-2: normal-high (plateau [fan sediment]) + // kd = 1e-2: normal (plateau) + let kd_i = 1.0e-2 / 4.0; + kd_i * kd_scale_i + } + }; + let g_func = |posi| { + if map_edge_factor(map_size_lg, posi) == 0.0 { + return 0.0; + } + // G = d* v_s / p_0, where + // v_s is the settling velocity of sediment grains + // p_0 is the mean precipitation rate + // d* is the sediment concentration ratio (between concentration near riverbed + // interface, and average concentration over the water column). + // d* varies with Rouse number which defines relative contribution of bed, + // suspended, and washed loads. + // + // G is typically on the order of 1 or greater. However, we are only guaranteed + // to converge for G ≤ 1, so we keep it in the chaos range of [0.12, + // 1.32]. + 1.0 + }; + let epsilon_0_func = |posi| { + // epsilon_0_scale is roughly [using Hack's Law with b = 2 and SPL without + // debris flow or hillslopes] equal to the ratio of the old to new + // area, to the power of -n_i. + let epsilon_0_scale_i = epsilon_0_scale(n_func(posi)); + if is_ocean_fn(posi) { + // marine: ε₀ = 2.078e-3 + let epsilon_0_i = 2.078e-3 / 4.0; + return epsilon_0_i * epsilon_0_scale_i; + } + let wposf = (uniform_idx_as_vec2(map_size_lg, posi) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + .map(|e| e as f64); + let turb_wposf = wposf + .mul(5_000.0 / continent_scale) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(turb_wposf_div); + let turb = Vec2::new( + gen_ctx.turb_x_nz.get(turb_wposf.into_array()), + gen_ctx.turb_y_nz.get(turb_wposf.into_array()), + ) * uplift_turb_scale + * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); + let turb_wposf = wposf + turb; + let uheight = gen_ctx + .uplift_nz + .get(turb_wposf.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let wposf3 = Vec3::new( + wposf.x, + wposf.y, + uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, + ); + let rock_strength = gen_ctx + .rock_strength_nz + .get(wposf3.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let center = 0.4; + let dmin = center - 0.05; + let dmax = center + 0.05; + let log_odds = |x: f64| logit(x) - logit(center); + let ustrength = logistic_cdf( + 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) + + 1.0 * log_odds(uheight.min(dmax).max(dmin)), + ); + // marine: ε₀ = 2.078e-3 + // San Gabriel Mountains: ε₀ = 3.18e-4 + // Oregon Coast Range: ε₀ = 2.68e-4 + // Frogs Hollow (peak production = 0.25): ε₀ = 1.41e-4 + // Point Reyes: ε₀ = 8.1e-5 + // Nunnock River (fractured granite, least weathered?): ε₀ = 5.3e-5 + let epsilon_0_i = ((1.0 - ustrength) * (2.078e-3 - 5.3e-5) + 5.3e-5) as f32 / 4.0; + epsilon_0_i * epsilon_0_scale_i + }; + let alpha_func = |posi| { + let alpha_scale_i = alpha_scale(n_func(posi)); + if is_ocean_fn(posi) { + // marine: α = 3.7e-2 + return 3.7e-2 * alpha_scale_i; + } + let wposf = (uniform_idx_as_vec2(map_size_lg, posi) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + .map(|e| e as f64); + let turb_wposf = wposf + .mul(5_000.0 / continent_scale) + .div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) + .div(turb_wposf_div); + let turb = Vec2::new( + gen_ctx.turb_x_nz.get(turb_wposf.into_array()), + gen_ctx.turb_y_nz.get(turb_wposf.into_array()), + ) * uplift_turb_scale + * TerrainChunkSize::RECT_SIZE.map(|e| e as f64); + let turb_wposf = wposf + turb; + let uheight = gen_ctx + .uplift_nz + .get(turb_wposf.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let wposf3 = Vec3::new( + wposf.x, + wposf.y, + uheight * CONFIG.mountain_scale as f64 * rock_strength_div_factor, + ); + let rock_strength = gen_ctx + .rock_strength_nz + .get(wposf3.into_array()) + .min(1.0) + .max(-1.0) + .mul(0.5) + .add(0.5); + let center = 0.4; + let dmin = center - 0.05; + let dmax = center + 0.05; + let log_odds = |x: f64| logit(x) - logit(center); + let ustrength = logistic_cdf( + 1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7)) + + 1.0 * log_odds(uheight.min(dmax).max(dmin)), + ); + // Frog Hollow (peak production = 0.25): α = 4.2e-2 + // San Gabriel Mountains: α = 3.8e-2 + // marine: α = 3.7e-2 + // Oregon Coast Range: α = 3e-2 + // Nunnock river (fractured granite, least weathered?): α = 2e-3 + // Point Reyes: α = 1.6e-2 + // The stronger the rock, the faster the decline in soil production. + let alpha_i = (ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32; + alpha_i * alpha_scale_i + }; + let uplift_fn = |posi| { + if is_ocean_fn(posi) { + return 0.0; + } + let height = (uplift_uniform[posi].1 - alt_old_min_uniform) as f64 + / (alt_old_max_uniform - alt_old_min_uniform) as f64; + + let height = height.mul(max_epsilon - min_epsilon).add(min_epsilon); + let height = erosion_factor(height); + assert!(height >= 0.0); + assert!(height <= 1.0); + + // u = 1e-3: normal-high (dike, mountain) + // u = 5e-4: normal (mid example in Yuan, average mountain uplift) + // u = 2e-4: low (low example in Yuan; known that lagoons etc. may have u ~ + // 0.05). u = 0: low (plateau [fan, altitude = 0.0]) + let height = height.mul(max_erosion_per_delta_t); + height as f64 + }; + let alt_func = |posi| { + if is_ocean_fn(posi) { + old_height(posi) + } else { + (old_height(posi) as f64 / CONFIG.mountain_scale as f64) as f32 - 0.5 + } + }; + // Perform some erosion. let (alt, basement) = if let Some(map) = parsed_world_file { (map.alt, map.basement) } else { let (alt, basement) = do_erosion( + map_size_lg, max_erosion_per_delta_t as f32, n_steps, &river_seed, @@ -990,6 +1062,7 @@ impl WorldSim { // Quick "small scale" erosion cycle in order to lower extreme angles. do_erosion( + map_size_lg, 1.0f32, n_small_steps, &river_seed, @@ -1013,7 +1086,12 @@ impl WorldSim { // Save map, if necessary. // NOTE: We wll always save a map with latest version. - let map = WorldFile::new(ModernMap { alt, basement }); + let map = WorldFile::new(ModernMap { + continent_scale_hack, + map_size_lg: map_size_lg.vec(), + alt, + basement, + }); (|| { if let FileOpts::Save = opts.world_file { use std::time::SystemTime; @@ -1050,13 +1128,19 @@ impl WorldSim { // Skip validation--we just performed a no-op conversion for this map, so it had // better be valid! - let ModernMap { alt, basement } = map.into_modern().unwrap(); + let ModernMap { + continent_scale_hack: _, + map_size_lg: _, + alt, + basement, + } = map.into_modern().unwrap(); // Additional small-scale eroson after map load, only used during testing. let (alt, basement) = if n_post_load_steps == 0 { (alt, basement) } else { do_erosion( + map_size_lg, 1.0f32, n_post_load_steps, &river_seed, @@ -1078,29 +1162,32 @@ impl WorldSim { ) }; - let is_ocean = get_oceans(|posi| alt[posi]); + let is_ocean = get_oceans(map_size_lg, |posi| alt[posi]); let is_ocean_fn = |posi: usize| is_ocean[posi]; - let mut dh = downhill(|posi| alt[posi], is_ocean_fn); - let (boundary_len, indirection, water_alt_pos, maxh) = get_lakes(|posi| alt[posi], &mut dh); + let mut dh = downhill(map_size_lg, |posi| alt[posi], is_ocean_fn); + let (boundary_len, indirection, water_alt_pos, maxh) = + get_lakes(map_size_lg, |posi| alt[posi], &mut dh); debug!(?maxh, "Max height"); let (mrec, mstack, mwrec) = { - let mut wh = vec![0.0; WORLD_SIZE.x * WORLD_SIZE.y]; + let mut wh = vec![0.0; map_size_lg.chunks_len()]; get_multi_rec( + map_size_lg, |posi| alt[posi], &dh, &water_alt_pos, &mut wh, - WORLD_SIZE.x, - WORLD_SIZE.y, + usize::from(map_size_lg.chunks().x), + usize::from(map_size_lg.chunks().y), TerrainChunkSize::RECT_SIZE.x as Compute, TerrainChunkSize::RECT_SIZE.y as Compute, maxh, ) }; - let flux_old = get_multi_drainage(&mstack, &mrec, &*mwrec, boundary_len); - let flux_rivers = get_drainage(&water_alt_pos, &dh, boundary_len); - // TODO: Make rivers work with multi-direction flux as well. - // let flux_rivers = flux_old.clone(); + let flux_old = get_multi_drainage(map_size_lg, &mstack, &mrec, &*mwrec, boundary_len); + // let flux_rivers = get_drainage(map_size_lg, &water_alt_pos, &dh, + // boundary_len); TODO: Make rivers work with multi-direction flux as + // well. + let flux_rivers = flux_old.clone(); let water_height_initial = |chunk_idx| { let indirection_idx = indirection[chunk_idx]; @@ -1150,13 +1237,21 @@ impl WorldSim { // may comment out this line and replace it with the commented-out code // below; however, there are no guarantees that this // will work correctly. - let water_alt = fill_sinks(water_height_initial, is_ocean_fn); - /* let water_alt = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let water_alt = fill_sinks(map_size_lg, water_height_initial, is_ocean_fn); + /* let water_alt = (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| water_height_initial(posi)) .collect::>(); */ - let rivers = get_rivers(&water_alt_pos, &water_alt, &dh, &indirection, &flux_rivers); + let rivers = get_rivers( + map_size_lg, + continent_scale_hack, + &water_alt_pos, + &water_alt, + &dh, + &indirection, + &flux_rivers, + ); let water_alt = indirection .par_iter() @@ -1195,11 +1290,15 @@ impl WorldSim { // Check whether any tiles around this tile are not water (since Lerp will // ensure that they are included). let pure_water = |posi: usize| { - let pos = uniform_idx_as_vec2(posi); + let pos = uniform_idx_as_vec2(map_size_lg, posi); for x in pos.x - 1..(pos.x + 1) + 1 { for y in pos.y - 1..(pos.y + 1) + 1 { - if x >= 0 && y >= 0 && x < WORLD_SIZE.x as i32 && y < WORLD_SIZE.y as i32 { - let posi = vec2_as_uniform_idx(Vec2::new(x, y)); + if x >= 0 + && y >= 0 + && x < map_size_lg.chunks().x as i32 + && y < map_size_lg.chunks().y as i32 + { + let posi = vec2_as_uniform_idx(map_size_lg, Vec2::new(x, y)); if !is_underwater(posi) { return false; } @@ -1214,7 +1313,7 @@ impl WorldSim { || { rayon::join( || { - uniform_noise(|posi, _| { + uniform_noise(map_size_lg, |posi, _| { if pure_water(posi) { None } else { @@ -1225,7 +1324,7 @@ impl WorldSim { }) }, || { - uniform_noise(|posi, _| { + uniform_noise(map_size_lg, |posi, _| { if pure_water(posi) { None } else { @@ -1238,7 +1337,7 @@ impl WorldSim { || { rayon::join( || { - uniform_noise(|posi, wposf| { + uniform_noise(map_size_lg, |posi, wposf| { if pure_water(posi) { None } else { @@ -1248,7 +1347,7 @@ impl WorldSim { }) }, || { - uniform_noise(|posi, wposf| { + uniform_noise(map_size_lg, |posi, wposf| { // Check whether any tiles around this tile are water. if pure_water(posi) { None @@ -1280,13 +1379,14 @@ impl WorldSim { rivers, }; - let chunks = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let chunks = (0..map_size_lg.chunks_len()) .into_par_iter() - .map(|i| SimChunk::generate(i, &gen_ctx, &gen_cdf)) + .map(|i| SimChunk::generate(map_size_lg, i, &gen_ctx, &gen_cdf)) .collect::>(); let mut this = Self { seed, + map_size_lg, max_height: maxh as f32, chunks, locations: Vec::new(), @@ -1301,21 +1401,122 @@ impl WorldSim { this } - pub fn get_size(&self) -> Vec2 { WORLD_SIZE.map(|e| e as u32) } + #[inline(always)] + pub const fn map_size_lg(&self) -> MapSizeLg { self.map_size_lg } + + pub fn get_size(&self) -> Vec2 { self.map_size_lg().chunks().map(u32::from) } /// Draw a map of the world based on chunk information. Returns a buffer of /// u32s. - pub fn get_map(&self, index: &Index) -> Vec { - let mut v = vec![0u32; WORLD_SIZE.x * WORLD_SIZE.y]; + pub fn get_map(&self, index: IndexRef) -> WorldMapMsg { + let mut map_config = MapConfig::orthographic( + self.map_size_lg(), + core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height), + ); + // Build a horizon map. + let scale_angle = |angle: Alt| { + (/* 0.0.max( */angle /* ) */ + .atan() + * ::FRAC_2_PI() + * 255.0) + .floor() as u8 + }; + let scale_height = |height: Alt| { + (/* 0.0.max( */height/*)*/ as Alt * 255.0 / self.max_height as Alt).floor() as u8 + }; + + let samples_data = { + let column_sample = ColumnGen::new(self); + (0..self.map_size_lg().chunks_len()) + .into_par_iter() + .map_init( + || Box::new(BlockGen::new(ColumnGen::new(self))), + |block_gen, posi| { + let wpos = uniform_idx_as_vec2(self.map_size_lg(), posi); + let mut sample = column_sample.get( + (uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + index) + )?; + let alt = sample.alt; + /* let z_cache = block_gen.get_z_cache(wpos); + sample.alt = alt.max(z_cache.get_z_limits(&mut block_gen).2); */ + sample.alt = alt.max(BlockGen::get_cliff_height( + &block_gen.column_gen, + &mut block_gen.column_cache, + wpos.map(|e| e as f32), + &sample.close_cliffs, + sample.cliff_hill, + 32.0, + index, + )); + sample.basement += sample.alt - alt; + // sample.water_level = CONFIG.sea_level.max(sample.water_level); + + Some(sample) + }, + ) + /* .map(|posi| { + let mut sample = column_sample.get( + uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ); + }) */ + .collect::>() + .into_boxed_slice() + }; + + let horizons = get_horizon_map( + self.map_size_lg(), + Aabr { + min: Vec2::zero(), + max: self.map_size_lg().chunks().map(|e| e as i32), + }, + CONFIG.sea_level, + CONFIG.sea_level + self.max_height, + |posi| { + /* let chunk = &self.chunks[posi]; + chunk.alt.max(chunk.water_alt) as Alt */ + let sample = samples_data[posi].as_ref(); + sample + .map(|s| s.alt.max(s.water_level)) + .unwrap_or(CONFIG.sea_level) + }, + |a| scale_angle(a.into()), + |h| scale_height(h.into()), + ) + .unwrap(); + + let mut v = vec![0u32; self.map_size_lg().chunks_len()]; + let mut alts = vec![0u32; self.map_size_lg().chunks_len()]; // TODO: Parallelize again. - MapConfig { - gain: self.max_height, - ..MapConfig::default() + map_config.is_shaded = false; + + map_config.generate( + |pos| sample_pos(&map_config, self, index, Some(&samples_data), pos), + |pos| sample_wpos(&map_config, self, pos), + |pos, (r, g, b, _a)| { + // We currently ignore alpha and replace it with the height at pos, scaled to + // u8. + let alt = sample_wpos( + &map_config, + self, + pos.map(|e| e as i32) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ); + let a = 0; //(alt.min(1.0).max(0.0) * 255.0) as u8; + + // NOTE: Safe by invariants on map_size_lg. + let posi = (pos.y << self.map_size_lg().vec().x) | pos.x; + v[posi] = u32::from_le_bytes([r, g, b, a]); + alts[posi] = (((alt.min(1.0).max(0.0) * 8191.0) as u32) & 0x1FFF) << 3; + }, + ); + WorldMapMsg { + dimensions_lg: self.map_size_lg().vec(), + sea_level: CONFIG.sea_level, + max_height: self.max_height, + rgba: v, + alt: alts, + horizons, } - .generate(&self, index, |pos, (r, g, b, a)| { - v[pos.y * WORLD_SIZE.x + pos.x] = u32::from_le_bytes([r, g, b, a]); - }); - v } /// Prepare the world for simulation @@ -1323,7 +1524,7 @@ impl WorldSim { let mut rng = self.rng.clone(); let cell_size = 16; - let grid_size = WORLD_SIZE / cell_size; + let grid_size = self.map_size_lg().chunks().map(usize::from) / cell_size; let loc_count = 100; let mut loc_grid = vec![None; grid_size.product()]; @@ -1391,7 +1592,7 @@ impl WorldSim { .par_iter_mut() .enumerate() .for_each(|(ij, chunk)| { - let chunk_pos = uniform_idx_as_vec2(ij); + let chunk_pos = uniform_idx_as_vec2(self.map_size_lg(), ij); let i = chunk_pos.x as usize; let j = chunk_pos.y as usize; let block_pos = Vec2::new( @@ -1431,10 +1632,10 @@ impl WorldSim { // Create waypoints const WAYPOINT_EVERY: usize = 16; let this = &self; - let waypoints = (0..WORLD_SIZE.x) + let waypoints = (0..this.map_size_lg().chunks().x) .step_by(WAYPOINT_EVERY) .map(|i| { - (0..WORLD_SIZE.y) + (0..this.map_size_lg().chunks().y) .step_by(WAYPOINT_EVERY) .map(move |j| (i, j)) }) @@ -1477,10 +1678,10 @@ impl WorldSim { pub fn get(&self, chunk_pos: Vec2) -> Option<&SimChunk> { if chunk_pos - .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) + .map2(self.map_size_lg().chunks(), |e, sz| e >= 0 && e < sz as i32) .reduce_and() { - Some(&self.chunks[vec2_as_uniform_idx(chunk_pos)]) + Some(&self.chunks[vec2_as_uniform_idx(self.map_size_lg(), chunk_pos)]) } else { None } @@ -1508,28 +1709,31 @@ impl WorldSim { } pub fn get_mut(&mut self, chunk_pos: Vec2) -> Option<&mut SimChunk> { + let map_size_lg = self.map_size_lg(); if chunk_pos - .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) + .map2(map_size_lg.chunks(), |e, sz| e >= 0 && e < sz as i32) .reduce_and() { - Some(&mut self.chunks[vec2_as_uniform_idx(chunk_pos)]) + Some(&mut self.chunks[vec2_as_uniform_idx(map_size_lg, chunk_pos)]) } else { None } } pub fn get_base_z(&self, chunk_pos: Vec2) -> Option { - if !chunk_pos - .map2(WORLD_SIZE, |e, sz| e > 0 && e < sz as i32 - 2) - .reduce_and() - { + let in_bounds = chunk_pos + .map2(self.map_size_lg().chunks(), |e, sz| { + e > 0 && e < sz as i32 - 2 + }) + .reduce_and(); + if !in_bounds { return None; } - let chunk_idx = vec2_as_uniform_idx(chunk_pos); - local_cells(chunk_idx) + let chunk_idx = vec2_as_uniform_idx(self.map_size_lg(), chunk_pos); + local_cells(self.map_size_lg(), chunk_idx) .flat_map(|neighbor_idx| { - let neighbor_pos = uniform_idx_as_vec2(neighbor_idx); + let neighbor_pos = uniform_idx_as_vec2(self.map_size_lg(), neighbor_idx); let neighbor_chunk = self.get(neighbor_pos); let river_kind = neighbor_chunk.and_then(|c| c.river.river_kind); let has_water = river_kind.is_some() && river_kind != Some(RiverKind::Ocean); @@ -1835,11 +2039,10 @@ pub struct RegionInfo { impl SimChunk { #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 - fn generate(posi: usize, gen_ctx: &GenCtx, gen_cdf: &GenCdf) -> Self { - let pos = uniform_idx_as_vec2(posi); + fn generate(map_size_lg: MapSizeLg, posi: usize, gen_ctx: &GenCtx, gen_cdf: &GenCdf) -> Self { + let pos = uniform_idx_as_vec2(map_size_lg, posi); let wposf = (pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)).map(|e| e as f64); - let _map_edge_factor = map_edge_factor(posi); let (_, chaos) = gen_cdf.chaos[posi]; let alt_pre = gen_cdf.alt[posi] as f32; let basement_pre = gen_cdf.basement[posi] as f32; @@ -1860,7 +2063,7 @@ impl SimChunk { // can always add a small x component). // // Not clear that we want this yet, let's see. - let latitude_uniform = (pos.y as f32 / WORLD_SIZE.y as f32).sub(0.5).mul(2.0); + let latitude_uniform = (pos.y as f32 / f32::from(self.map_size_lg().chunks().y)).sub(0.5).mul(2.0); // Even less granular--if this matters we can make the sign affect the quantiy slightly. let abs_lat_uniform = latitude_uniform.abs(); */ @@ -1893,7 +2096,7 @@ impl SimChunk { panic!("Uh... shouldn't this never, ever happen?"); } else { Some( - uniform_idx_as_vec2(downhill_pre as usize) + uniform_idx_as_vec2(map_size_lg, downhill_pre as usize) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), ) }; diff --git a/world/src/sim/util.rs b/world/src/sim/util.rs index 679bce94b0..3fda529d13 100644 --- a/world/src/sim/util.rs +++ b/world/src/sim/util.rs @@ -1,6 +1,8 @@ -use super::WORLD_SIZE; use bitvec::prelude::{bitbox, BitBox}; -use common::{terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + terrain::{neighbors, uniform_idx_as_vec2, vec2_as_uniform_idx, MapSizeLg, TerrainChunkSize}, + vol::RectVolSize, +}; use noise::{MultiFractal, NoiseFn, Perlin, Point2, Point3, Point4, Seedable}; use num::Float; use rayon::prelude::*; @@ -8,14 +10,15 @@ use std::{f32, f64, ops::Mul, u32}; use vek::*; /// Calculates the smallest distance along an axis (x, y) from an edge of -/// the world. This value is maximal at WORLD_SIZE / 2 and minimized at the -/// extremes (0 or WORLD_SIZE on one or more axes). It then divides the -/// quantity by cell_size, so the final result is 1 when we are not in a cell -/// along the edge of the world, and ranges between 0 and 1 otherwise (lower -/// when the chunk is closer to the edge). -pub fn map_edge_factor(posi: usize) -> f32 { - uniform_idx_as_vec2(posi) - .map2(WORLD_SIZE.map(|e| e as i32), |e, sz| { +/// the world. This value is maximal at map_size_lg.chunks() / 2 and +/// minimized at the +/// extremes (0 or map_size_lg.chunks() on one or more axes). It then divides +/// the quantity by cell_size, so the final result is 1 when we are not in a +/// cell along the edge of the world, and ranges between 0 and 1 otherwise +/// (lower when the chunk is closer to the edge). +pub fn map_edge_factor(map_size_lg: MapSizeLg, posi: usize) -> f32 { + uniform_idx_as_vec2(map_size_lg, posi) + .map2(map_size_lg.chunks().map(i32::from), |e, sz| { (sz / 2 - (e - sz / 2).abs()) as f32 / (16.0 / 1024.0 * sz as f32) }) .reduce_partial_min() @@ -118,22 +121,12 @@ pub fn cdf_irwin_hall(weights: &[f32; N], samples: [f32; N]) -> /// returned by the noise function applied to every chunk in the game). Second /// component is the cached value of the noise function that generated the /// index. -/// -/// NOTE: Length should always be WORLD_SIZE.x * WORLD_SIZE.y. pub type InverseCdf = Box<[(f32, F)]>; -/// Computes the position Vec2 of a SimChunk from an index, where the index was -/// generated by uniform_noise. -pub fn uniform_idx_as_vec2(idx: usize) -> Vec2 { - Vec2::new((idx % WORLD_SIZE.x) as i32, (idx / WORLD_SIZE.x) as i32) -} - -/// Computes the index of a Vec2 of a SimChunk from a position, where the index -/// is generated by uniform_noise. NOTE: Both components of idx should be -/// in-bounds! -pub fn vec2_as_uniform_idx(idx: Vec2) -> usize { - (idx.y as usize * WORLD_SIZE.x + idx.x as usize) as usize -} +/// NOTE: First component is estimated horizon angles at each chunk; second +/// component is estimated heights of maximal occluder at each chunk (used +/// for making shadows volumetric). +pub type HorizonMap = (Vec, Vec); /// Compute inverse cumulative distribution function for arbitrary function f, /// the hard way. We pre-generate noise values prior to worldgen, then sort @@ -173,15 +166,17 @@ pub fn vec2_as_uniform_idx(idx: Vec2) -> usize { /// value actually uses the same one we were using here easier). Also returns /// the "inverted index" pointing from a position to a noise. pub fn uniform_noise( + map_size_lg: MapSizeLg, f: impl Fn(usize, Vec2) -> Option + Sync, ) -> (InverseCdf, Box<[(usize, F)]>) { - let mut noise = (0..WORLD_SIZE.x * WORLD_SIZE.y) + let mut noise = (0..map_size_lg.chunks_len()) .into_par_iter() .filter_map(|i| { f( i, - (uniform_idx_as_vec2(i) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) - .map(|e| e as f64), + (uniform_idx_as_vec2(map_size_lg, i) + * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)) + .map(|e| e as f64), ) .map(|res| (i, res)) }) @@ -197,7 +192,7 @@ pub fn uniform_noise( // position of the noise in the sorted vector (divided by the vector length). // This guarantees a uniform distribution among the samples (excluding those // that returned None, which will remain at zero). - let mut uniform_noise = vec![(0.0, F::nan()); WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice(); + let mut uniform_noise = vec![(0.0, F::nan()); map_size_lg.chunks_len()].into_boxed_slice(); // NOTE: Consider using try_into here and elsewhere in this function, since // i32::MAX technically doesn't fit in an f32 (even if we should never reach // that limit). @@ -218,8 +213,8 @@ pub fn uniform_noise( /// its top-right/down-right/down neighbors, the twelve chunks surrounding this /// box (its "perimeter") are also inspected. #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 -pub fn local_cells(posi: usize) -> impl Clone + Iterator { - let pos = uniform_idx_as_vec2(posi); +pub fn local_cells(map_size_lg: MapSizeLg, posi: usize) -> impl Clone + Iterator { + let pos = uniform_idx_as_vec2(map_size_lg, posi); // NOTE: want to keep this such that the chunk index is in ascending order! let grid_size = 3i32; let grid_bounds = 2 * grid_size + 1; @@ -231,51 +226,35 @@ pub fn local_cells(posi: usize) -> impl Clone + Iterator { pos.y + (index / grid_bounds) - grid_size, ) }) - .filter(|pos| { - pos.x >= 0 && pos.y >= 0 && pos.x < WORLD_SIZE.x as i32 && pos.y < WORLD_SIZE.y as i32 + .filter(move |pos| { + pos.x >= 0 + && pos.y >= 0 + && pos.x < map_size_lg.chunks().x as i32 + && pos.y < map_size_lg.chunks().y as i32 }) - .map(vec2_as_uniform_idx) -} - -// NOTE: want to keep this such that the chunk index is in ascending order! -pub const NEIGHBOR_DELTA: [(i32, i32); 8] = [ - (-1, -1), - (0, -1), - (1, -1), - (-1, 0), - (1, 0), - (-1, 1), - (0, 1), - (1, 1), -]; - -/// Iterate through all cells adjacent to a chunk. -pub fn neighbors(posi: usize) -> impl Clone + Iterator { - let pos = uniform_idx_as_vec2(posi); - NEIGHBOR_DELTA - .iter() - .map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y)) - .filter(|pos| { - pos.x >= 0 && pos.y >= 0 && pos.x < WORLD_SIZE.x as i32 && pos.y < WORLD_SIZE.y as i32 - }) - .map(vec2_as_uniform_idx) + .map(move |e| vec2_as_uniform_idx(map_size_lg, e)) } // Note that we should already have okay cache locality since we have a grid. -pub fn uphill<'a>(dh: &'a [isize], posi: usize) -> impl Clone + Iterator + 'a { - neighbors(posi).filter(move |&posj| dh[posj] == posi as isize) +pub fn uphill<'a>( + map_size_lg: MapSizeLg, + dh: &'a [isize], + posi: usize, +) -> impl Clone + Iterator + 'a { + neighbors(map_size_lg, posi).filter(move |&posj| dh[posj] == posi as isize) } /// Compute the neighbor "most downhill" from all chunks. /// /// TODO: See if allocating in advance is worthwhile. pub fn downhill( + map_size_lg: MapSizeLg, h: impl Fn(usize) -> F + Sync, is_ocean: impl Fn(usize) -> bool + Sync, ) -> Box<[isize]> { // Constructs not only the list of downhill nodes, but also computes an ordering // (visiting nodes in order from roots to leaves). - (0..WORLD_SIZE.x * WORLD_SIZE.y) + (0..map_size_lg.chunks_len()) .into_par_iter() .map(|posi| { let nh = h(posi); @@ -284,7 +263,7 @@ pub fn downhill( } else { let mut best = -1; let mut besth = nh; - for nposi in neighbors(posi) { + for nposi in neighbors(map_size_lg, posi) { let nbh = h(nposi); if nbh < besth { besth = nbh; @@ -298,48 +277,174 @@ pub fn downhill( .into_boxed_slice() } +/* /// Bilinear interpolation. +/// +/// Linear interpolation in both directions (i.e. quadratic interpolation). +fn get_interpolated_bilinear(&self, pos: Vec2, mut f: F) -> Option + where + T: Copy + Default + Signed + Float + Add + Mul, + F: FnMut(Vec2) -> Option, +{ + // (i) Find downhill for all four points. + // (ii) Compute distance from each downhill point and do linear interpolation on + // their heights. (iii) Compute distance between each neighboring point + // and do linear interpolation on their distance-interpolated + // heights. + + // See http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1990A%26A...239..443S&defaultprint=YES&page_ind=0&filetype=.pdf + // + // Note that these are only guaranteed monotone in one dimension; fortunately, + // that is sufficient for our purposes. + let pos = pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { + e as f64 / sz as f64 + }); + + // Orient the chunk in the direction of the most downhill point of the four. If + // there is no "most downhill" point, then we don't care. + let x0 = pos.map2(Vec2::new(0, 0), |e, q| e.max(0.0) as i32 + q); + let y0 = f(x0)?; + + let x1 = pos.map2(Vec2::new(1, 0), |e, q| e.max(0.0) as i32 + q); + let y1 = f(x1)?; + + let x2 = pos.map2(Vec2::new(0, 1), |e, q| e.max(0.0) as i32 + q); + let y2 = f(x2)?; + + let x3 = pos.map2(Vec2::new(1, 1), |e, q| e.max(0.0) as i32 + q); + let y3 = f(x3)?; + + let z0 = y0 + .mul(1.0 - pos.x.fract() as f32) + .mul(1.0 - pos.y.fract() as f32); + let z1 = y1.mul(pos.x.fract() as f32).mul(1.0 - pos.y.fract() as f32); + let z2 = y2.mul(1.0 - pos.x.fract() as f32).mul(pos.y.fract() as f32); + let z3 = y3.mul(pos.x.fract() as f32).mul(pos.y.fract() as f32); + + Some(z0 + z1 + z2 + z3) +} */ + /// Find all ocean tiles from a height map, using an inductive definition of /// ocean as one of: /// - posi is at the side of the world (map_edge_factor(posi) == 0.0) /// - posi has a neighboring ocean tile, and has a height below sea level /// (oldh(posi) <= 0.0). -pub fn get_oceans(oldh: impl Fn(usize) -> F + Sync) -> BitBox { +pub fn get_oceans(map_size_lg: MapSizeLg, oldh: impl Fn(usize) -> F + Sync) -> BitBox { // We can mark tiles as ocean candidates by scanning row by row, since the top // edge is ocean, the sides are connected to it, and any subsequent ocean // tiles must be connected to it. - let mut is_ocean = bitbox![0; WORLD_SIZE.x * WORLD_SIZE.y]; + let mut is_ocean = bitbox![0; map_size_lg.chunks_len()]; let mut stack = Vec::new(); let mut do_push = |pos| { - let posi = vec2_as_uniform_idx(pos); + let posi = vec2_as_uniform_idx(map_size_lg, pos); if oldh(posi) <= F::zero() { stack.push(posi); } }; - for x in 0..WORLD_SIZE.x as i32 { + for x in 0..map_size_lg.chunks().x as i32 { do_push(Vec2::new(x, 0)); - do_push(Vec2::new(x, WORLD_SIZE.y as i32 - 1)); + do_push(Vec2::new(x, map_size_lg.chunks().y as i32 - 1)); } - for y in 1..WORLD_SIZE.y as i32 - 1 { + for y in 1..map_size_lg.chunks().y as i32 - 1 { do_push(Vec2::new(0, y)); - do_push(Vec2::new(WORLD_SIZE.x as i32 - 1, y)); + do_push(Vec2::new(map_size_lg.chunks().x as i32 - 1, y)); } while let Some(chunk_idx) = stack.pop() { - // println!("Ocean chunk {:?}: {:?}", uniform_idx_as_vec2(chunk_idx), - // oldh(chunk_idx)); + // println!("Ocean chunk {:?}: {:?}", uniform_idx_as_vec2(map_size_lg, + // chunk_idx), oldh(chunk_idx)); let mut is_ocean = is_ocean.get_mut(chunk_idx).unwrap(); if *is_ocean { continue; } *is_ocean = true; - stack.extend(neighbors(chunk_idx).filter(|&neighbor_idx| { - // println!("Ocean neighbor: {:?}: {:?}", uniform_idx_as_vec2(neighbor_idx), - // oldh(neighbor_idx)); + stack.extend(neighbors(map_size_lg, chunk_idx).filter(|&neighbor_idx| { + // println!("Ocean neighbor: {:?}: {:?}", uniform_idx_as_vec2(map_size_lg, + // neighbor_idx), oldh(neighbor_idx)); oldh(neighbor_idx) <= F::zero() })); } is_ocean } +/// Finds the horizon map for sunlight for the given chunks. +pub fn get_horizon_map( + map_size_lg: MapSizeLg, + bounds: Aabr, + minh: F, + maxh: F, + h: impl Fn(usize) -> F + Sync, + to_angle: impl Fn(F) -> A + Sync, + to_height: impl Fn(F) -> H + Sync, +) -> Result<[HorizonMap; 2], ()> { + if maxh < minh { + // maxh must be greater than minh + return Err(()); + } + let map_size = Vec2::::from(bounds.size()).map(|e| e as usize); + let map_len = map_size.product(); + + // Now, do the raymarching. + let chunk_x = if let Vec2 { x: Some(x), .. } = TerrainChunkSize::RECT_SIZE.map(F::from) { + x + } else { + return Err(()); + }; + // let epsilon = F::epsilon() * if let x = F::from(map_size.x) { x } else { + // return Err(()) }; + let march = |dx: isize, maxdx: fn(isize, map_size_lg: MapSizeLg) -> isize| { + let mut angles = Vec::with_capacity(map_len); + let mut heights = Vec::with_capacity(map_len); + (0..map_len) + .into_par_iter() + .map(|posi| { + let wposi = + bounds.min + Vec2::new((posi % map_size.x) as i32, (posi / map_size.x) as i32); + if wposi.reduce_partial_min() < 0 + || wposi.x as usize >= usize::from(map_size_lg.chunks().x) + || wposi.y as usize >= usize::from(map_size_lg.chunks().y) + { + return (to_angle(F::zero()), to_height(F::zero())); + } + let posi = vec2_as_uniform_idx(map_size_lg, wposi); + // March in the given direction. + let maxdx = maxdx(wposi.x as isize, map_size_lg); + let mut slope = F::zero(); + let h0 = h(posi); + let h = if h0 < minh { + F::zero() + } else { + let mut max_height = F::zero(); + let maxdz = maxh - h0; + let posi = posi as isize; + for deltax in 1..maxdx { + let posj = (posi + deltax * dx) as usize; + let deltax = chunk_x * F::from(deltax).unwrap(); + let h_j_est = slope * deltax; + if h_j_est > maxdz { + break; + } + let h_j_act = h(posj) - h0; + if + /* h_j_est - h_j_act <= epsilon */ + h_j_est <= h_j_act { + slope = h_j_act / deltax; + max_height = h_j_act; + } + } + h0 - minh + max_height + }; + let a = slope; + (to_angle(a), to_height(h)) + }) + .unzip_into_vecs(&mut angles, &mut heights); + (angles, heights) + }; + let west = march(-1, |x, _| x); + let east = march(1, |x, map_size_lg| { + (usize::from(map_size_lg.chunks().x) - x as usize) as isize + }); + Ok([west, east]) +} + /// A 2-dimensional vector, for internal use. type Vector2 = [T; 2]; /// A 3-dimensional vector, for internal use. diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index cc367448ed..ab5b15e638 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -3,9 +3,10 @@ use crate::{ column::ColumnSample, sim::WorldSim, site::settlement::building::{ - archetype::keep::{Attr, Keep as KeepArchetype}, + archetype::keep::{Attr, FlagColor, Keep as KeepArchetype, StoneColor}, Archetype, Ori, }, + IndexRef, }; use common::{ generation::ChunkSupplement, @@ -14,6 +15,7 @@ use common::{ }; use core::f32; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; struct Keep { @@ -47,6 +49,9 @@ pub struct GenCtx<'a, R: Rng> { rng: &'a mut R, } +#[derive(Deserialize, Serialize)] +pub struct Colors; + impl Castle { #[allow(clippy::let_and_return)] // TODO: Pending review in #587 pub fn generate(wpos: Vec2, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self { @@ -167,6 +172,7 @@ impl Castle { pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), @@ -273,14 +279,14 @@ impl Castle { let keep_archetype = KeepArchetype { flag_color: if self.evil { - Rgb::new(80, 10, 130) + FlagColor::Evil } else { - Rgb::new(200, 80, 40) + FlagColor::Good }, stone_color: if self.evil { - Rgb::new(65, 60, 55) + StoneColor::Evil } else { - Rgb::new(100, 100, 110) + StoneColor::Good }, }; @@ -294,6 +300,7 @@ impl Castle { } let mut mask = keep_archetype.draw( + index, Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt, wall_dist, border_pos, @@ -323,6 +330,7 @@ impl Castle { let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs()); mask = mask.resolve_with(keep_archetype.draw( + index, if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() { wpos - tower_wpos } else { @@ -364,6 +372,7 @@ impl Castle { let border_pos = (keep_wpos - wpos).xy().map(|e| e.abs()); mask = mask.resolve_with(keep_archetype.draw( + index, if (keep_wpos.x - wpos.x).abs() < (keep_wpos.y - wpos.y).abs() { wpos - keep_wpos } else { diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index b34619c487..4b3fe2f8a7 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -5,6 +5,7 @@ use crate::{ sim::WorldSim, site::BlockMask, util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, + IndexRef, }; use common::{ assets, @@ -20,6 +21,7 @@ use core::{f32, hash::BuildHasherDefault}; use fxhash::FxHasher64; use lazy_static::lazy_static; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use std::sync::Arc; use vek::*; @@ -37,6 +39,11 @@ pub struct GenCtx<'a, R: Rng> { rng: &'a mut R, } +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub stone: (u8, u8, u8), +} + const ALT_OFFSET: i32 = -2; const LEVELS: usize = 5; @@ -80,6 +87,7 @@ impl Dungeon { pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), @@ -112,7 +120,14 @@ impl Dungeon { .ok() .copied() .map(|sb| { - block_from_structure(sb, spos, self.origin, self.seed, col_sample) + block_from_structure( + index, + sb, + spos, + self.origin, + self.seed, + col_sample, + ) }) .unwrap_or(None) { @@ -125,7 +140,7 @@ impl Dungeon { for floor in &self.floors { z -= floor.total_depth(); - let mut sampler = floor.col_sampler(rpos, z); + let mut sampler = floor.col_sampler(index, rpos, z); for rz in 0..floor.total_depth() { if let Some(block) = sampler(rz).finish() { @@ -574,16 +589,29 @@ impl Floor { } #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 - pub fn col_sampler(&self, pos: Vec2, floor_z: i32) -> impl FnMut(i32) -> BlockMask + '_ { + pub fn col_sampler<'a>( + &'a self, + index: IndexRef<'a>, + pos: Vec2, + floor_z: i32, + ) -> impl FnMut(i32) -> BlockMask + 'a { let rpos = pos - self.tile_offset * TILE_SIZE; let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; let rtile_pos = rpos - tile_center; + let colors = &index.colors.site.dungeon; + let empty = BlockMask::new(Block::empty(), 1); let make_staircase = move |pos: Vec3, radius: f32, inner_radius: f32, stretch: f32| { - let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5); + let stone = BlockMask::new( + Block::new( + BlockKind::Normal, + /* Rgb::new(150, 150, 175) */ colors.stone.into(), + ), + 5, + ); if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) { stone diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 82c83b2183..01f9985f4d 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -10,15 +10,23 @@ pub use self::{ settlement::Settlement, }; -use crate::column::ColumnSample; +use crate::{column::ColumnSample, IndexRef}; use common::{ generation::ChunkSupplement, terrain::Block, vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, }; use rand::Rng; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub castle: castle::Colors, + pub dungeon: dungeon::Colors, + pub settlement: settlement::Colors, +} + pub struct SpawnRules { pub trees: bool, } @@ -86,14 +94,15 @@ impl Site { pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { match &self.kind { - SiteKind::Settlement(s) => s.apply_to(wpos2d, get_column, vol), - SiteKind::Dungeon(d) => d.apply_to(wpos2d, get_column, vol), - SiteKind::Castle(c) => c.apply_to(wpos2d, get_column, vol), + SiteKind::Settlement(s) => s.apply_to(index, wpos2d, get_column, vol), + SiteKind::Dungeon(d) => d.apply_to(index, wpos2d, get_column, vol), + SiteKind::Castle(c) => c.apply_to(index, wpos2d, get_column, vol), } } diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 970e3a4698..aa15f844e8 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -4,64 +4,103 @@ use super::{super::skeleton::*, Archetype}; use crate::{ site::BlockMask, util::{RandomField, Sampler}, + IndexRef, }; use common::{ + make_case_elim, terrain::{Block, BlockKind}, vol::Vox, }; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; -pub struct ColorTheme { - roof: Rgb, - wall: Rgb, - support: Rgb, +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub foundation: (u8, u8, u8), + pub floor: (u8, u8, u8), + pub roof: roof_color::PureCases<(u8, u8, u8)>, + pub wall: wall_color::PureCases<(u8, u8, u8)>, + pub support: support_color::PureCases<(u8, u8, u8)>, } -const ROOF_COLORS: &[Rgb] = &[ - // Rgb::new(0x1D, 0x4D, 0x45), - // Rgb::new(0xB3, 0x7D, 0x60), - // Rgb::new(0xAC, 0x5D, 0x26), - // Rgb::new(0x32, 0x46, 0x6B), - // Rgb::new(0x2B, 0x19, 0x0F), - // Rgb::new(0x93, 0x78, 0x51), - // Rgb::new(0x92, 0x57, 0x24), - // Rgb::new(0x4A, 0x4E, 0x4E), - // Rgb::new(0x2F, 0x32, 0x47), - // Rgb::new(0x8F, 0x35, 0x43), - // Rgb::new(0x6D, 0x1E, 0x3A), - // Rgb::new(0x6D, 0xA7, 0x80), - // Rgb::new(0x4F, 0xA0, 0x95), - // Rgb::new(0xE2, 0xB9, 0x99), - // Rgb::new(0x7A, 0x30, 0x22), - // Rgb::new(0x4A, 0x06, 0x08), - // Rgb::new(0x8E, 0xB4, 0x57), - Rgb::new(0x99, 0x5E, 0x54), - Rgb::new(0x43, 0x63, 0x64), - Rgb::new(0x76, 0x6D, 0x68), - Rgb::new(0x7B, 0x41, 0x61), - Rgb::new(0x52, 0x20, 0x20), - Rgb::new(0x1A, 0x4A, 0x59), - Rgb::new(0xCC, 0x76, 0x4E), +pub struct ColorTheme { + roof: RoofColor, + wall: WallColor, + support: SupportColor, +} + +make_case_elim!( + roof_color, + #[repr(u32)] + #[derive(Clone, Copy)] + pub enum RoofColor { + Roof1 = 0, + Roof2 = 1, + Roof3 = 2, + Roof4 = 3, + Roof5 = 4, + Roof6 = 5, + Roof7 = 6, + } +); + +make_case_elim!( + wall_color, + #[repr(u32)] + #[derive(Clone, Copy)] + pub enum WallColor { + Wall1 = 0, + Wall2 = 1, + Wall3 = 2, + Wall4 = 3, + Wall5 = 4, + Wall6 = 5, + Wall7 = 6, + Wall8 = 7, + Wall9 = 8, + } +); + +make_case_elim!( + support_color, + #[repr(u32)] + #[derive(Clone, Copy)] + pub enum SupportColor { + Support1 = 0, + Support2 = 1, + Support3 = 2, + Support4 = 3, + } +); + +const ROOF_COLORS: [RoofColor; roof_color::NUM_VARIANTS] = [ + RoofColor::Roof1, + RoofColor::Roof2, + RoofColor::Roof3, + RoofColor::Roof4, + RoofColor::Roof4, + RoofColor::Roof6, + RoofColor::Roof7, ]; -const WALL_COLORS: &[Rgb] = &[ - Rgb::new(200, 180, 150), - Rgb::new(0xB8, 0xB4, 0xA4), - Rgb::new(0x76, 0x6D, 0x68), - Rgb::new(0xF3, 0xC9, 0x8F), - Rgb::new(0xD3, 0xB7, 0x99), - Rgb::new(0xE1, 0xAB, 0x91), - Rgb::new(0x82, 0x57, 0x4C), - Rgb::new(0xB9, 0x96, 0x77), - Rgb::new(0xAE, 0x8D, 0x9C), +const WALL_COLORS: [WallColor; wall_color::NUM_VARIANTS] = [ + WallColor::Wall1, + WallColor::Wall2, + WallColor::Wall3, + WallColor::Wall4, + WallColor::Wall5, + WallColor::Wall6, + WallColor::Wall7, + WallColor::Wall8, + WallColor::Wall9, ]; -const SUPPORT_COLORS: &[Rgb] = &[ - Rgb::new(60, 45, 30), - Rgb::new(0x65, 0x55, 0x56), - Rgb::new(0x53, 0x33, 0x13), - Rgb::new(0x58, 0x42, 0x33), +const SUPPORT_COLORS: [SupportColor; support_color::NUM_VARIANTS] = [ + SupportColor::Support1, + SupportColor::Support2, + SupportColor::Support3, + SupportColor::Support4, ]; pub struct House { @@ -210,6 +249,7 @@ impl Archetype for House { #[allow(clippy::int_plus_one)] // TODO: Pending review in #587 fn draw( &self, + index: IndexRef, _pos: Vec3, dist: i32, bound_offset: Vec2, @@ -220,6 +260,11 @@ impl Archetype for House { _len: i32, attr: &Self::Attr, ) -> BlockMask { + let colors = &index.colors.site.settlement.building.archetype.house; + let roof_color = *self.colors.roof.elim_case_pure(&colors.roof); + let wall_color = *self.colors.wall.elim_case_pure(&colors.wall); + let support_color = *self.colors.support.elim_case_pure(&colors.support); + let profile = Vec2::new(bound_offset.x, z); let make_meta = |ori| { @@ -240,6 +285,7 @@ impl Archetype for House { BlockMask::new( Block::new( BlockKind::Normal, + // TODO: Clarify exactly how this affects the color. Rgb::new(r, g, b) .map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)), ), @@ -253,11 +299,11 @@ impl Archetype for House { let foundation_layer = internal_layer + 1; let floor_layer = foundation_layer + 1; - let foundation = make_block((100, 100, 100)).with_priority(foundation_layer); - let log = make_block(self.colors.support.into_tuple()); - let floor = make_block((100, 75, 50)); - let wall = make_block(self.colors.wall.into_tuple()).with_priority(facade_layer); - let roof = make_block(self.colors.roof.into_tuple()).with_priority(facade_layer - 1); + let foundation = make_block(colors.foundation).with_priority(foundation_layer); + let log = make_block(support_color); + let floor = make_block(colors.floor); + let wall = make_block(wall_color).with_priority(facade_layer); + let roof = make_block(roof_color).with_priority(facade_layer - 1); let empty = BlockMask::nothing(); let internal = BlockMask::new(Block::empty(), internal_layer); let end_window = BlockMask::new( diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 2028fb75d0..160772f6e4 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -2,17 +2,29 @@ use super::{super::skeleton::*, Archetype}; use crate::{ site::BlockMask, util::{RandomField, Sampler}, + IndexRef, }; use common::{ + make_case_elim, terrain::{Block, BlockKind}, vol::Vox, }; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub brick_base: (u8, u8, u8), + pub floor_base: (u8, u8, u8), + pub pole: (u8, u8, u8), + pub flag: flag_color::PureCases<(u8, u8, u8)>, + pub stone: stone_color::PureCases<(u8, u8, u8)>, +} + pub struct Keep { - pub flag_color: Rgb, - pub stone_color: Rgb, + pub flag_color: FlagColor, + pub stone_color: StoneColor, } pub struct Attr { @@ -24,6 +36,24 @@ pub struct Attr { pub has_doors: bool, } +make_case_elim!( + flag_color, + #[repr(u32)] + pub enum FlagColor { + Good = 0, + Evil = 1, + } +); + +make_case_elim!( + stone_color, + #[repr(u32)] + pub enum StoneColor { + Good = 0, + Evil = 1, + } +); + impl Archetype for Keep { type Attr = Attr; @@ -71,8 +101,8 @@ impl Archetype for Keep { ( Self { - flag_color: Rgb::new(200, 80, 40), - stone_color: Rgb::new(100, 100, 110), + flag_color: FlagColor::Good, + stone_color: StoneColor::Good, }, skel, ) @@ -81,6 +111,7 @@ impl Archetype for Keep { #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 fn draw( &self, + index: IndexRef, pos: Vec3, _dist: i32, bound_offset: Vec2, @@ -91,6 +122,11 @@ impl Archetype for Keep { _len: i32, attr: &Self::Attr, ) -> BlockMask { + let dungeon_stone = index.colors.site.dungeon.stone; + let colors = &index.colors.site.settlement.building.archetype.keep; + let flag_color = self.flag_color.elim_case_pure(&colors.flag); + let stone_color = self.stone_color.elim_case_pure(&colors.stone); + let profile = Vec2::new(bound_offset.x, z); let weak_layer = 1; @@ -118,30 +154,35 @@ impl Archetype for Keep { let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1); let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24; - let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex); + let foundation = make_block( + colors.brick_base.0 + brick_tex, + colors.brick_base.1 + brick_tex, + colors.brick_base.2 + brick_tex, + ); let wall = make_block( - self.stone_color.r + brick_tex, - self.stone_color.g + brick_tex, - self.stone_color.b + brick_tex, + stone_color.0 + brick_tex, + stone_color.1 + brick_tex, + stone_color.2 + brick_tex, ); let window = BlockMask::new( Block::new(BlockKind::Window1, make_meta(ori.flip())), normal_layer, ); let floor = make_block( - 80 + (pos.y.abs() % 2) as u8 * 15, - 60 + (pos.y.abs() % 2) as u8 * 15, - 10 + (pos.y.abs() % 2) as u8 * 15, + colors.floor_base.0 + (pos.y.abs() % 2) as u8 * 15, + colors.floor_base.1 + (pos.y.abs() % 2) as u8 * 15, + colors.floor_base.2 + (pos.y.abs() % 2) as u8 * 15, ) .with_priority(important_layer); - let pole = make_block(90, 70, 50).with_priority(important_layer); - let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b) - .with_priority(important_layer); + let pole = + make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer); + let flag = + make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer); let internal = BlockMask::new(Block::empty(), internal_layer); let empty = BlockMask::nothing(); let make_staircase = move |pos: Vec3, radius: f32, inner_radius: f32, stretch: f32| { - let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5); + let stone = BlockMask::new(Block::new(BlockKind::Normal, dungeon_stone.into()), 5); if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) { stone diff --git a/world/src/site/settlement/building/archetype/mod.rs b/world/src/site/settlement/building/archetype/mod.rs index d20e943ef2..a8e67d5aff 100644 --- a/world/src/site/settlement/building/archetype/mod.rs +++ b/world/src/site/settlement/building/archetype/mod.rs @@ -2,10 +2,17 @@ pub mod house; pub mod keep; use super::skeleton::*; -use crate::site::BlockMask; +use crate::{site::BlockMask, IndexRef}; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub house: house::Colors, + pub keep: keep::Colors, +} + pub trait Archetype { type Attr; @@ -16,6 +23,7 @@ pub trait Archetype { #[allow(clippy::too_many_arguments)] fn draw( &self, + index: IndexRef, pos: Vec3, dist: i32, bound_offset: Vec2, diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 0f2e6096cd..d58ee8ad5c 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -7,10 +7,17 @@ pub use self::{ skeleton::*, }; +use crate::IndexRef; use common::terrain::Block; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub archetype: archetype::Colors, +} + pub struct Building { skel: Skeleton, archetype: A, @@ -46,13 +53,14 @@ impl Building { } } - pub fn sample(&self, pos: Vec3) -> Option { + pub fn sample(&self, index: IndexRef, pos: Vec3) -> Option { let rpos = pos - self.origin; self.skel .sample_closest( rpos, |pos, dist, bound_offset, center_offset, ori, branch| { self.archetype.draw( + index, pos, dist, bound_offset, diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 4ead8da0bc..50d22b3c52 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -10,6 +10,7 @@ use crate::{ column::ColumnSample, sim::WorldSim, util::{RandomField, Sampler, StructureGen2d}, + IndexRef, }; use common::{ assets, @@ -25,9 +26,30 @@ use common::{ use fxhash::FxHasher64; use hashbrown::{HashMap, HashSet}; use rand::prelude::*; +use serde::{Deserialize, Serialize}; use std::{collections::VecDeque, f32, hash::BuildHasherDefault}; use vek::*; +#[derive(Deserialize, Serialize)] +pub struct Colors { + pub building: building::Colors, + + pub plot_town_path: (u8, u8, u8), + + pub plot_field_dirt: (u8, u8, u8), + pub plot_field_mound: (u8, u8, u8), + + pub wall_low: (u8, u8, u8), + pub wall_high: (u8, u8, u8), + + pub tower_color: (u8, u8, u8), + + pub plot_dirt: (u8, u8, u8), + pub plot_grass: (u8, u8, u8), + pub plot_water: (u8, u8, u8), + pub plot_town: (u8, u8, u8), +} + #[allow(dead_code)] pub fn gradient(line: [Vec2; 2]) -> f32 { let r = (line[0].y - line[1].y) / (line[0].x - line[1].x); @@ -109,10 +131,10 @@ impl Structure { } } - pub fn sample(&self, rpos: Vec3) -> Option { + pub fn sample(&self, index: IndexRef, rpos: Vec3) -> Option { match &self.kind { - StructureKind::House(house) => house.sample(rpos), - StructureKind::Keep(keep) => keep.sample(rpos), + StructureKind::House(house) => house.sample(index, rpos), + StructureKind::Keep(keep) => keep.sample(index, rpos), } } } @@ -527,10 +549,13 @@ impl Settlement { #[allow(clippy::modulo_one)] // TODO: Pending review in #587 pub fn apply_to<'a>( &'a self, + index: IndexRef, wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { + let colors = &index.colors.site.settlement; + for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { let offs = Vec2::new(x, y); @@ -586,47 +611,6 @@ impl Settlement { } } - // Paths - // if let Some((WayKind::Path, dist, nearest)) = sample.way { - // let inset = -1; - - // // Try to use the column at the centre of the path for sampling to make - // them // flatter - // let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) - // .unwrap_or(col_sample); - // let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist { - // ( - // ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * - // 5.0, ((1.0 - ((water_dist + 2.0) * - // 0.3).min(0.0).cos().abs()) - // * (col.riverless_alt + 5.0 - col.alt).max(0.0) - // * 1.75 - // + 3.0) as i32, - // ) - // } else { - // (0.0, 3) - // }; - // let surface_z = (col.riverless_alt + bridge_offset).floor() as i32; - - // for z in inset - depth..inset { - // let _ = vol.set( - // Vec3::new(offs.x, offs.y, surface_z + z), - // if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { - // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, - // 100), 8)) } else { - // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50, - // 30), 8)) }, - // ); - // } - // let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1); - // for z in inset..inset + head_space { - // let pos = Vec3::new(offs.x, offs.y, surface_z + z); - // if vol.get(pos).unwrap().kind() != BlockKind::Water { - // let _ = vol.set(pos, Block::empty()); - // } - // } - // // Ground colour - // } else { let mut surface_block = None; @@ -634,9 +618,9 @@ impl Settlement { |seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n; let color = match sample.plot { - Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)), - Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), - Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), + Some(Plot::Dirt) => Some(colors.plot_dirt.into()), + Some(Plot::Grass) => Some(colors.plot_grass.into()), + Some(Plot::Water) => Some(colors.plot_water.into()), //Some(Plot::Town { district }) => None, Some(Plot::Town { .. }) => { if let Some((_, path_nearest, _, _)) = col_sample.path { @@ -659,13 +643,16 @@ impl Settlement { } } - Some(Rgb::new(100, 95, 65).map2(Rgb::iota(), |e: u8, i: i32| { - e.saturating_add( - (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1) - as u8, - ) - .saturating_sub(8) - })) + Some(Rgb::from(colors.plot_town_path).map2( + Rgb::iota(), + |e: u8, i: i32| { + e.saturating_add( + (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1) + as u8, + ) + .saturating_sub(8) + }, + )) }, Some(Plot::Field { seed, crop, .. }) => { let furrow_dirs = [ @@ -677,12 +664,13 @@ impl Settlement { let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()]; let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2; - let dirt = Rgb::new(80, 55, 35).map(|e| { + let dirt = Rgb::::from(colors.plot_field_dirt).map(|e| { e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32) as u8 }); - let mound = - Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| { + let mound = Rgb::::from(colors.plot_field_mound) + .map(|e| e + roll(0, 8) as u8) + .map(|e| { e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32)) % 32) as u8 }); @@ -769,8 +757,8 @@ impl Settlement { // Walls if let Some((WayKind::Wall, dist, _)) = sample.way { let color = Lerp::lerp( - Rgb::new(130i32, 100, 0), - Rgb::new(90, 70, 50), + Rgb::::from(colors.wall_low).map(i32::from), + Rgb::::from(colors.wall_high).map(i32::from), (RandomField::new(0).get(wpos2d.into()) % 256) as f32 / 256.0, ) .map(|e| (e % 256) as u8); @@ -797,7 +785,7 @@ impl Settlement { for z in -2..16 { let _ = vol.set( Vec3::new(offs.x, offs.y, surface_z + z), - Block::new(BlockKind::Normal, Rgb::new(50, 50, 50)), + Block::new(BlockKind::Normal, colors.tower_color.into()), ); } } @@ -832,7 +820,7 @@ impl Settlement { let wpos = Vec3::from(self.origin) + rpos; let coffs = wpos - Vec3::from(wpos2d); - if let Some(block) = structure.sample(rpos) { + if let Some(block) = structure.sample(index, rpos) { let _ = vol.set(coffs, block); } } @@ -938,30 +926,24 @@ impl Settlement { } } - pub fn get_color(&self, pos: Vec2) -> Option> { + pub fn get_color(&self, index: IndexRef, pos: Vec2) -> Option> { + let colors = &index.colors.site.settlement; + let sample = self.land.get_at_block(pos); - // match sample.tower { - // Some((Tower::Wall, _)) => return Some(Rgb::new(50, 50, 50)), - // _ => {}, - // } - - // match sample.way { - // Some((WayKind::Path, _, _)) => return Some(Rgb::new(90, 70, 50)), - // Some((WayKind::Hedge, _, _)) => return Some(Rgb::new(0, 150, 0)), - // Some((WayKind::Wall, _, _)) => return Some(Rgb::new(60, 60, 60)), - // _ => {}, - // } - match sample.plot { - Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)), - Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)), - Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)), + Some(Plot::Dirt) => return Some(colors.plot_dirt.into()), + Some(Plot::Grass) => return Some(colors.plot_grass.into()), + Some(Plot::Water) => return Some(colors.plot_water.into()), Some(Plot::Town { .. }) => { - return Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| { - e.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8) + return Some( + Rgb::from(colors.plot_town).map2(Rgb::iota(), |e: u8, i: i32| { + e.saturating_add( + (self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8, + ) .saturating_sub(8) - })); + }), + ); }, Some(Plot::Field { seed, .. }) => { let furrow_dirs = [ @@ -972,6 +954,12 @@ impl Settlement { ]; let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()]; let furrow = (pos * furrow_dir).sum().rem_euclid(6) < 3; + // NOTE: Very hard to understand how to make this dynamically configurable. The + // base values can easily cause the others to go out of range, and there's some + // weird scaling going on. For now, we just let these remain hardcoded. + // + // FIXME: Rewrite this so that validity is not so heavily dependent on the exact + // color values. return Some(Rgb::new( if furrow { 100 @@ -1003,6 +991,8 @@ pub enum Crop { Sunflower, } +// NOTE: No support for struct variants in make_case_elim yet, unfortunately, so +// we can't use it. #[derive(Copy, Clone, PartialEq)] pub enum Plot { Hazard,