diff --git a/CHANGELOG.md b/CHANGELOG.md index 628f68ae7d..31e03ccaf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added waypoints next to dungeons - Made players spawn in towns - Added non-uniform block heights +- Added `/sudo` command +- Added a Level of Detail (LoD) system for terrain sprites and entities ### Changed diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE.ron index 5c9e7b21d8..c2b672e579 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE.ron @@ -252,7 +252,8 @@ Viel Spaß in der Welt von Veloren, Abenteurer!"#, "hud.settings.invert_mouse_y_axis": "Maus Y-Achse invertieren", "hud.settings.free_look_behavior": "Freies Umsehen", - "hud.settings.view_distance": "Sichtweite", + "hud.settings.view_distance": "Gelände Sichtweite", + "hud.settings.sprites_view_distance": "Objekt Sichtweite", "hud.settings.maximum_fps": "Maximale FPS", "hud.settings.fov": "Sichtfeld (Grad)", "hud.settings.gamma": "Gamma", diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 6e39d56c43..ef5b00b544 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -250,6 +250,7 @@ Enjoy your stay in the World of Veloren."#, "hud.settings.free_look_behavior": "Free look behavior", "hud.settings.view_distance": "View Distance", + "hud.settings.sprites_view_distance": "Sprites View Distance", "hud.settings.maximum_fps": "Maximum FPS", "hud.settings.fov": "Field of View (deg)", "hud.settings.gamma": "Gamma", diff --git a/assets/voxygen/shaders/figure-vert.glsl b/assets/voxygen/shaders/figure-vert.glsl index d6bf3bafca..3e13da9599 100644 --- a/assets/voxygen/shaders/figure-vert.glsl +++ b/assets/voxygen/shaders/figure-vert.glsl @@ -3,11 +3,10 @@ #include #include -in vec3 v_pos; +in uint v_pos_norm; in vec3 v_norm; -in vec3 v_col; -in float v_ao; -in uint v_bone_idx; +in uint v_col; +in uint v_ao_bone; layout (std140) uniform u_locals { @@ -24,6 +23,7 @@ struct BoneData { layout (std140) uniform u_bones { + // Warning: might not actually be 16 elements long. Don't index out of bounds! BoneData bones[16]; }; @@ -36,20 +36,27 @@ flat out vec3 f_norm; void main() { // Pre-calculate bone matrix - mat4 combined_mat = model_mat * bones[v_bone_idx].bone_mat; + uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; + mat4 combined_mat = model_mat * bones[bone_idx].bone_mat; + + vec3 pos = vec3((uvec3(v_pos_norm) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) - 128.0; f_pos = ( combined_mat * - vec4(v_pos, 1)).xyz; + vec4(pos, 1)).xyz; - f_col = srgb_to_linear(v_col); + f_col = srgb_to_linear(vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0); - f_ao = v_ao; + f_ao = float(v_ao_bone & 0x3u) / 4.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 >> 24) & 0x7u]; // Calculate normal here rather than for each pixel in the fragment shader f_norm = normalize(( combined_mat * - vec4(v_norm, 0.0) + vec4(norm, 0.0) ).xyz); // Also precalculate shadow texture and estimated terrain altitude. diff --git a/assets/voxygen/shaders/include/globals.glsl b/assets/voxygen/shaders/include/globals.glsl index b35fab1228..895492a23a 100644 --- a/assets/voxygen/shaders/include/globals.glsl +++ b/assets/voxygen/shaders/include/globals.glsl @@ -16,6 +16,7 @@ uniform u_globals { // 0 - FirstPerson // 1 - ThirdPerson uint cam_mode; + float sprite_render_distance; }; // Specifies the pattern used in the player dithering @@ -26,4 +27,4 @@ mat4 threshold_matrix = mat4( vec4(16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0) ); float distance_divider = 2; -float shadow_dithering = 0.5; \ No newline at end of file +float shadow_dithering = 0.5; diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl index 6672ae80e5..8786793de9 100644 --- a/assets/voxygen/shaders/sprite-frag.glsl +++ b/assets/voxygen/shaders/sprite-frag.glsl @@ -14,7 +14,6 @@ out vec4 tgt_color; #include #include -const float RENDER_DIST = 112.0; const float FADE_DIST = 32.0; void main() { @@ -98,5 +97,5 @@ void main() { vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); - tgt_color = vec4(color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (RENDER_DIST - FADE_DIST)) / FADE_DIST, 0, 1)); + 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 11475a9155..68d76d048a 100644 --- a/assets/voxygen/shaders/sprite-vert.glsl +++ b/assets/voxygen/shaders/sprite-vert.glsl @@ -4,9 +4,8 @@ #include in vec3 v_pos; -in vec3 v_norm; -in vec3 v_col; -in float v_ao; +in uint v_col; +in uint v_norm_ao; in vec4 inst_mat0; in vec4 inst_mat1; in vec4 inst_mat2; @@ -32,7 +31,7 @@ void main() { vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz; - f_pos.z -= min(32.0, pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0)); + f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); // Wind waving f_pos += inst_wind_sway * vec3( @@ -41,10 +40,13 @@ void main() { 0.0 ) * pow(abs(v_pos.z) * SCALE, 1.3) * 0.2; - f_norm = (inst_mat * vec4(v_norm, 0)).xyz; + // 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; - f_col = srgb_to_linear(v_col) * srgb_to_linear(inst_col); - f_ao = v_ao; + 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; // Select glowing if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) { diff --git a/assets/voxygen/voxel/sprite/chests/chest.vox b/assets/voxygen/voxel/sprite/chests/chest.vox index 5e6fe09e6d..a53f8947d6 100644 --- a/assets/voxygen/voxel/sprite/chests/chest.vox +++ b/assets/voxygen/voxel/sprite/chests/chest.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:964619ec04d1d799d37fd4f7e6f7a9ff330cf10172baed9d551542172e7be84e -size 5092 +oid sha256:f89820cd9b32bd4e9cd84f1f3d43530c0bbec1aa14649c9b5ef5f67b13fe458b +size 7516 diff --git a/assets/voxygen/voxel/sprite/chests/chest_dark.vox b/assets/voxygen/voxel/sprite/chests/chest_dark.vox index d46c0c1563..32c2519e09 100644 --- a/assets/voxygen/voxel/sprite/chests/chest_dark.vox +++ b/assets/voxygen/voxel/sprite/chests/chest_dark.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e31c3e53a5df23ac194dee70496d73d56a12966889acfb87ac94173a5672f83e -size 4720 +oid sha256:aa0d381595de0624a7ea3f6e0115836d3aa97344d537bee7d56ec237724411e1 +size 7516 diff --git a/assets/voxygen/voxel/sprite/chests/chest_demon.vox b/assets/voxygen/voxel/sprite/chests/chest_demon.vox index aa213775bf..389f97748c 100644 --- a/assets/voxygen/voxel/sprite/chests/chest_demon.vox +++ b/assets/voxygen/voxel/sprite/chests/chest_demon.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1e58917b892e8209484b8f0596b97ad0a6b7dee17916eac06d66fa4fdeac332 -size 4712 +oid sha256:ad4452aa4e64713bb5c9f92ff2e91f1567c55fa8e11b6ce1583903ef6135d5d5 +size 7508 diff --git a/assets/voxygen/voxel/sprite/chests/chest_gold.vox b/assets/voxygen/voxel/sprite/chests/chest_gold.vox index 78f23a17bc..7b33d85c83 100644 --- a/assets/voxygen/voxel/sprite/chests/chest_gold.vox +++ b/assets/voxygen/voxel/sprite/chests/chest_gold.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9e3e07cbcd142777f0ffbfc5a1d03ed278c37a7f15bf28052146d50376135d0 -size 4720 +oid sha256:324624d423c4b627923096e936dda4f496cd1306363a8f14a07d10e8ede30eb2 +size 7516 diff --git a/assets/voxygen/voxel/sprite/chests/chest_light.vox b/assets/voxygen/voxel/sprite/chests/chest_light.vox index fdb9bc30c3..91a258f479 100644 --- a/assets/voxygen/voxel/sprite/chests/chest_light.vox +++ b/assets/voxygen/voxel/sprite/chests/chest_light.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97c3df0823abea550fae4a8346961e5ced496629328c476391c8f2c2d6410075 -size 4720 +oid sha256:5705ae83a952b8cfcbe2d945c43c663cc8fb99af0d7be55530a3ca1c0453ce75 +size 7516 diff --git a/assets/voxygen/voxel/sprite/chests/chest_skull.vox b/assets/voxygen/voxel/sprite/chests/chest_skull.vox index 36bbba84aa..ac5f2d23d6 100644 --- a/assets/voxygen/voxel/sprite/chests/chest_skull.vox +++ b/assets/voxygen/voxel/sprite/chests/chest_skull.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:597406e569312f237439d8c6b51fe1ace98b499dfe2cbc95566dee3ad1c037d4 -size 4708 +oid sha256:ab10e8033706d30681733c2329c6a0ab86242a358041f3aa39f4302463feada6 +size 7504 diff --git a/assets/voxygen/voxel/sprite/chests/chest_vines.vox b/assets/voxygen/voxel/sprite/chests/chest_vines.vox index 418c3cb5dc..ae5ca39aa2 100644 --- a/assets/voxygen/voxel/sprite/chests/chest_vines.vox +++ b/assets/voxygen/voxel/sprite/chests/chest_vines.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e0fc1699b25c1ac778001d6a9c498700abbe2a5a1ce9ef9ac7b0136c8db0043 -size 4996 +oid sha256:939eae713abcbc364ed6a4dda765a4752d54b2f0f69873f8d6fb75ad3952a76a +size 7792 diff --git a/common/src/figure/mod.rs b/common/src/figure/mod.rs index 0be11c7bf6..4fb39425cd 100644 --- a/common/src/figure/mod.rs +++ b/common/src/figure/mod.rs @@ -2,7 +2,9 @@ pub mod cell; pub mod mat_cell; pub use mat_cell::Material; -use self::{cell::Cell, mat_cell::MatCell}; +// Reexport +pub use self::{cell::Cell, mat_cell::MatCell}; + use crate::{ vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol}, volumes::dyna::Dyna, diff --git a/common/src/vol.rs b/common/src/vol.rs index 8740e60917..85626c37d7 100644 --- a/common/src/vol.rs +++ b/common/src/vol.rs @@ -1,4 +1,4 @@ -use crate::ray::Ray; +use crate::{ray::Ray, volumes::scaled::Scaled}; use std::fmt::Debug; use vek::*; @@ -24,6 +24,13 @@ pub trait Vox: Sized + Clone + PartialEq { pub trait BaseVol { type Vox: Vox; type Error: Debug; + + fn scaled_by(&self, scale: Vec3) -> Scaled + where + Self: Sized, + { + Scaled { inner: self, scale } + } } /// Implementing `BaseVol` for any `&'a BaseVol` makes it possible to implement @@ -159,6 +166,7 @@ where /// Unfortunately we can't just implement `IntoIterator` in this generic way /// because it's defined in another crate. That's actually the only reason why /// the trait `IntoFullVolIterator` exists. +// TODO: See whether relaxed orphan rules permit this to be replaced now impl<'a, T: 'a + SizedVol> IntoFullVolIterator<'a> for &'a T where Self: IntoVolIterator<'a>, diff --git a/common/src/volumes/mod.rs b/common/src/volumes/mod.rs index b84e75b7c8..6e8a9d6f10 100644 --- a/common/src/volumes/mod.rs +++ b/common/src/volumes/mod.rs @@ -1,4 +1,5 @@ pub mod chunk; pub mod dyna; +pub mod scaled; pub mod vol_grid_2d; pub mod vol_grid_3d; diff --git a/common/src/volumes/scaled.rs b/common/src/volumes/scaled.rs new file mode 100644 index 0000000000..44caa0bf51 --- /dev/null +++ b/common/src/volumes/scaled.rs @@ -0,0 +1,53 @@ +use crate::vol::{BaseVol, ReadVol, SizedVol, Vox}; +use vek::*; + +pub struct Scaled<'a, V> { + pub inner: &'a V, + pub scale: Vec3, +} + +impl<'a, V: BaseVol> BaseVol for Scaled<'a, V> { + type Error = V::Error; + type Vox = V::Vox; +} + +impl<'a, V: ReadVol> ReadVol for Scaled<'a, V> { + #[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_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) + .contains(&(pos[i] + *p)) + }) + }; + range_iter(0) + .map(|i| range_iter(1).map(move |j| range_iter(2).map(move |k| Vec3::new(i, j, k)))) + .flatten() + .flatten() + .map(|offs| self.inner.get(pos + offs)) + .find(|vox| vox.as_ref().map(|v| !v.is_empty()).unwrap_or(false)) + .unwrap_or_else(|| self.inner.get(pos)) + } +} + +impl<'a, V: SizedVol> SizedVol for Scaled<'a, V> { + #[inline(always)] + fn lower_bound(&self) -> Vec3 { + self.inner + .lower_bound() + .map2(self.scale, |e, scale| (e as f32 * scale).floor() as i32) + } + + #[inline(always)] + fn upper_bound(&self) -> Vec3 { + self.inner + .upper_bound() + .map2(self.scale, |e, scale| (e as f32 * scale).ceil() as i32 + 1) + } +} diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7bc96dde8f..f8df738d0a 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -40,12 +40,14 @@ pub struct ChatCommand { /// * `&mut Server` - the `Server` instance executing the command. /// * `EcsEntity` - an `Entity` corresponding to the player that invoked the /// command. + /// * `EcsEntity` - an `Entity` for the player on whom the command is + /// invoked. This differs from the previous argument when using /sudo /// * `String` - a `String` containing the part of the command after the /// keyword. /// * `&ChatCommand` - the command to execute with the above arguments. /// Handler functions must parse arguments from the the given `String` /// (`scan_fmt!` is included for this purpose). - handler: fn(&mut Server, EcsEntity, String, &ChatCommand), + handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand), } impl ChatCommand { @@ -55,7 +57,7 @@ impl ChatCommand { arg_fmt: &'static str, help_string: &'static str, needs_admin: bool, - handler: fn(&mut Server, EcsEntity, String, &ChatCommand), + handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand), ) -> Self { Self { keyword, @@ -80,10 +82,10 @@ impl ChatCommand { ); return; } else { - (self.handler)(server, entity, args, self); + (self.handler)(server, entity, entity, args, self); } } else { - (self.handler)(server, entity, args, self); + (self.handler)(server, entity, entity, args, self); } } } @@ -261,79 +263,125 @@ lazy_static! { true, handle_debug, ), + ChatCommand::new( + "sudo", + "{} {} {/.*/}", + "/sudo / [args...] : Run command as if you were another player", + true, + handle_sudo, + ), ]; } -fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { +fn handle_give( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + _action: &ChatCommand, +) { if let Ok(item) = assets::load_cloned(&args) { server .state .ecs() .write_storage::() - .get_mut(entity) + .get_mut(target) .map(|inv| inv.push(item)); let _ = server .state .ecs() .write_storage::() .insert( - entity, + target, comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given), ); } else { - server.notify_client(entity, ServerMsg::private(String::from("Invalid item!"))); + server.notify_client(client, ServerMsg::private(String::from("Invalid item!"))); } } -fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_jump( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { - match server.state.read_component_cloned::(entity) { + match server.state.read_component_cloned::(target) { Some(current_pos) => { server .state - .write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); - server.state.write_component(entity, comp::ForceUpdate); + .write_component(target, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); + server.state.write_component(target, comp::ForceUpdate); }, None => server.notify_client( - entity, + client, ServerMsg::private(String::from("You have no position.")), ), } } } -fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_goto( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { if server .state - .read_component_cloned::(entity) + .read_component_cloned::(target) .is_some() { server .state - .write_component(entity, comp::Pos(Vec3::new(x, y, z))); - server.state.write_component(entity, comp::ForceUpdate); + .write_component(target, comp::Pos(Vec3::new(x, y, z))); + server.state.write_component(target, comp::ForceUpdate); } else { server.notify_client( - entity, + client, ServerMsg::private(String::from("You have no position.")), ); } } else { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); } } -fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { +fn handle_kill( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { + let reason = if client == target { + comp::HealthSource::Suicide + } else { + if let Some(uid) = server.state.read_storage::().get(client) { + comp::HealthSource::Attack { by: *uid } + } else { + comp::HealthSource::Command + } + }; server .state .ecs_mut() .write_storage::() - .get_mut(entity) - .map(|s| s.health.set_to(0, comp::HealthSource::Suicide)); + .get_mut(target) + .map(|s| s.health.set_to(0, reason)); } -fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_time( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + args: String, + action: &ChatCommand, +) { let time = scan_fmt_some!(&args, action.arg_fmt, String); let new_time = match time.as_ref().map(|s| s.as_str()) { Some("midnight") => NaiveTime::from_hms(0, 0, 0), @@ -349,7 +397,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch Ok(time) => time, Err(_) => { server.notify_client( - entity, + client, ServerMsg::private(format!("'{}' is not a valid time.", n)), ); return; @@ -368,7 +416,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch Some(time) => format!("It is {}", time.format("%H:%M").to_string()), None => String::from("Unknown Time"), }; - server.notify_client(entity, ServerMsg::private(msg)); + server.notify_client(client, ServerMsg::private(msg)); return; }, }; @@ -377,7 +425,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch new_time.num_seconds_from_midnight() as f64; server.notify_client( - entity, + client, ServerMsg::private(format!( "Time changed to: {}", new_time.format("%H:%M").to_string() @@ -385,43 +433,55 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch ); } -fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_health( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { if let Some(stats) = server .state .ecs() .write_storage::() - .get_mut(entity) + .get_mut(target) { stats.health.set_to(hp, comp::HealthSource::Command); } else { server.notify_client( - entity, + client, ServerMsg::private(String::from("You have no health.")), ); } } else { server.notify_client( - entity, + client, ServerMsg::private(String::from("You must specify health amount!")), ); } } -fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_alias( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { server .state .ecs_mut() .write_storage::() - .get_mut(entity) + .get_mut(target) .map(|player| player.alias = alias); // Update name on client player lists let ecs = server.state.ecs(); if let (Some(uid), Some(player)) = ( - ecs.read_storage::().get(entity), - ecs.read_storage::().get(entity), + ecs.read_storage::().get(target), + ecs.read_storage::().get(target), ) { let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias( (*uid).into(), @@ -430,60 +490,72 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C server.state.notify_registered_clients(msg); } } else { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); } } -fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_tp( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { let ecs = server.state.ecs(); let opt_player = (&ecs.entities(), &ecs.read_storage::()) .join() .find(|(_, player)| player.alias == alias) .map(|(entity, _)| entity); - match server.state.read_component_cloned::(entity) { + match server.state.read_component_cloned::(target) { Some(_pos) => match opt_player { Some(player) => match server.state.read_component_cloned::(player) { Some(pos) => { - server.state.write_component(entity, pos); - server.state.write_component(entity, comp::ForceUpdate); + server.state.write_component(target, pos); + server.state.write_component(target, comp::ForceUpdate); }, None => server.notify_client( - entity, + client, ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)), ), }, None => { server.notify_client( - entity, + client, ServerMsg::private(format!("Player '{}' not found!", alias)), ); server.notify_client( - entity, + client, ServerMsg::private(String::from(action.help_string)), ); }, }, None => { - server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); + server.notify_client(client, ServerMsg::private(format!("You have no position!"))); }, } } else { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); } } -fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_spawn( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { match scan_fmt_some!(&args, action.arg_fmt, String, npc::NpcBody, String) { (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount) => { - if let Some(alignment) = parse_alignment(entity, &opt_align) { + if let Some(alignment) = parse_alignment(target, &opt_align) { let amount = opt_amount .and_then(|a| a.parse().ok()) .filter(|x| *x > 0) .unwrap_or(1) .min(10); - match server.state.read_component_cloned::(entity) { + match server.state.read_component_cloned::(target) { Some(pos) => { let agent = if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { @@ -517,7 +589,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C if let Some(uid) = server.state.ecs().uid_from_entity(new_entity) { server.notify_client( - entity, + client, ServerMsg::private( format!("Spawned entity with ID: {}", uid).to_owned(), ), @@ -525,24 +597,30 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C } } server.notify_client( - entity, + client, ServerMsg::private(format!("Spawned {} entities", amount).to_owned()), ); }, None => server.notify_client( - entity, + client, ServerMsg::private("You have no position!".to_owned()), ), } } }, _ => { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); }, } } -fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { +fn handle_players( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { let ecs = server.state.ecs(); let players = ecs.read_storage::(); let count = players.join().count(); @@ -560,26 +638,32 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action s }); - server.notify_client(entity, ServerMsg::private(header_message + &player_list)); + server.notify_client(client, ServerMsg::private(header_message + &player_list)); } else { - server.notify_client(entity, ServerMsg::private(header_message)); + server.notify_client(client, ServerMsg::private(header_message)); } } -fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { +fn handle_build( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { if server .state .read_storage::() - .get(entity) + .get(target) .is_some() { server .state .ecs() .write_storage::() - .remove(entity); + .remove(target); server.notify_client( - entity, + client, ServerMsg::private(String::from("Toggled off build mode!")), ); } else { @@ -587,18 +671,24 @@ fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: .state .ecs() .write_storage::() - .insert(entity, comp::CanBuild); + .insert(target, comp::CanBuild); server.notify_client( - entity, + client, ServerMsg::private(String::from("Toggled on build mode!")), ); } } -fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { +fn handle_help( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { for cmd in CHAT_COMMANDS.iter() { - if !cmd.needs_admin || server.entity_is_admin(entity) { - server.notify_client(entity, ServerMsg::private(String::from(cmd.help_string))); + if !cmd.needs_admin || server.entity_is_admin(client) { + server.notify_client(client, ServerMsg::private(String::from(cmd.help_string))); } } } @@ -613,7 +703,13 @@ fn parse_alignment(owner: EcsEntity, alignment: &str) -> Option } } -fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { +fn handle_killnpcs( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { let ecs = server.state.ecs(); let mut stats = ecs.write_storage::(); let players = ecs.read_storage::(); @@ -627,23 +723,29 @@ fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _actio } else { "No NPCs on server.".to_string() }; - server.notify_client(entity, ServerMsg::private(text)); + server.notify_client(client, ServerMsg::private(text)); } -fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { +fn handle_object( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + _action: &ChatCommand, +) { let obj_type = scan_fmt!(&args, _action.arg_fmt, String); let pos = server .state .ecs() .read_storage::() - .get(entity) + .get(target) .copied(); let ori = server .state .ecs() .read_storage::() - .get(entity) + .get(target) .copied(); /*let builder = server.state .create_object(pos, ori, obj_type) @@ -701,7 +803,7 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: Ok("crafting_bench") => comp::object::Body::CraftingBench, _ => { return server.notify_client( - entity, + client, ServerMsg::private(String::from("Object not found!")), ); }, @@ -723,18 +825,24 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: )) .build(); server.notify_client( - entity, + client, ServerMsg::private(format!( "Spawned: {}", obj_str_res.unwrap_or("") )), ); } else { - server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); + server.notify_client(client, ServerMsg::private(format!("You have no position!"))); } } -fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_light( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { let (opt_r, opt_g, opt_b, opt_x, opt_y, opt_z, opt_s) = scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32, f32, f32, f32); @@ -756,7 +864,7 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C .state .ecs() .read_storage::() - .get(entity) + .get(target) .copied(); if let Some(pos) = pos { server @@ -767,19 +875,25 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C .with(comp::ForceUpdate) .with(light_emitter) .build(); - server.notify_client(entity, ServerMsg::private(format!("Spawned object."))); + server.notify_client(client, ServerMsg::private(format!("Spawned object."))); } else { - server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); + server.notify_client(client, ServerMsg::private(format!("You have no position!"))); } } -fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_lantern( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { let opt_s = scan_fmt_some!(&args, action.arg_fmt, f32); if server .state .read_storage::() - .get(entity) + .get(target) .is_some() { if let Some(s) = opt_s { @@ -787,11 +901,11 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: .state .ecs() .write_storage::() - .get_mut(entity) + .get_mut(target) { light.strength = s.max(0.1).min(10.0); server.notify_client( - entity, + client, ServerMsg::private(String::from("You adjusted flame strength.")), ); } @@ -800,9 +914,9 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: .state .ecs() .write_storage::() - .remove(entity); + .remove(target); server.notify_client( - entity, + client, ServerMsg::private(String::from("You put out the lantern.")), ); } @@ -811,7 +925,7 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: .state .ecs() .write_storage::() - .insert(entity, comp::LightEmitter { + .insert(target, comp::LightEmitter { offset: Vec3::new(0.5, 0.2, 0.8), col: Rgb::new(1.0, 0.75, 0.3), strength: if let Some(s) = opt_s { @@ -822,50 +936,68 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: }); server.notify_client( - entity, + client, ServerMsg::private(String::from("You lit your lantern.")), ); } } -fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_explosion( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { let power = scan_fmt!(&args, action.arg_fmt, f32).unwrap_or(8.0); let ecs = server.state.ecs(); - match server.state.read_component_cloned::(entity) { + match server.state.read_component_cloned::(target) { Some(pos) => { ecs.read_resource::>() .emit_now(ServerEvent::Explosion { pos: pos.0, power, - owner: ecs.read_storage::().get(entity).copied(), + owner: ecs.read_storage::().get(target).copied(), }) }, None => server.notify_client( - entity, + client, ServerMsg::private(String::from("You have no position!")), ), } } -fn handle_waypoint(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - match server.state.read_component_cloned::(entity) { +fn handle_waypoint( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { + match server.state.read_component_cloned::(target) { Some(pos) => { let _ = server .state .ecs() .write_storage::() - .insert(entity, comp::Waypoint::new(pos.0)); - server.notify_client(entity, ServerMsg::private(String::from("Waypoint set!"))); + .insert(target, comp::Waypoint::new(pos.0)); + server.notify_client(client, ServerMsg::private(String::from("Waypoint set!"))); }, None => server.notify_client( - entity, + client, ServerMsg::private(String::from("You have no position!")), ), } } -fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_adminify( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + args: String, + action: &ChatCommand, +) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { let ecs = server.state.ecs(); let opt_player = (&ecs.entities(), &ecs.read_storage::()) @@ -883,18 +1015,31 @@ fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action: }, None => { server.notify_client( - entity, + client, ServerMsg::private(format!("Player '{}' not found!", alias)), ); - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); }, } } else { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); } } -fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_tell( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { + if client != target { + server.notify_client( + client, + ServerMsg::tell(String::from("It's rude to impersonate people")), + ); + return; + } if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { let ecs = server.state.ecs(); let msg = &args[alias.len()..args.len()]; @@ -903,11 +1048,11 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch .find(|(_, player)| player.alias == alias) .map(|(entity, _)| entity) { - if player != entity { + if player != target { if msg.len() > 1 { if let Some(name) = ecs .read_storage::() - .get(entity) + .get(target) .map(|s| s.alias.clone()) { server.notify_client( @@ -915,53 +1060,60 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch ServerMsg::tell(format!("[{}] tells:{}", name, msg)), ); server.notify_client( - entity, + client, ServerMsg::tell(format!("To [{}]:{}", alias, msg)), ); } else { server.notify_client( - entity, + client, ServerMsg::private(String::from("Failed to send message.")), ); } } else { server.notify_client( - entity, + client, ServerMsg::private(format!("[{}] wants to talk to you.", alias)), ); } } else { server.notify_client( - entity, + client, ServerMsg::private(format!("You can't /tell yourself.")), ); } } else { server.notify_client( - entity, + client, ServerMsg::private(format!("Player '{}' not found!", alias)), ); } } else { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); } } #[cfg(not(feature = "worldgen"))] fn handle_debug_column( server: &mut Server, - entity: EcsEntity, + client: EcsEntity, + target: EcsEntity, _args: String, _action: &ChatCommand, ) { server.notify_client( - entity, + client, ServerMsg::private(String::from("Unsupported without worldgen enabled")), ); } #[cfg(feature = "worldgen")] -fn handle_debug_column(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_debug_column( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + args: String, + action: &ChatCommand, +) { let sim = server.world.sim(); let sampler = server.world.sample_columns(); if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) { @@ -1020,15 +1172,15 @@ spawn_rate {:?} "#, )) }; if let Some(s) = foo() { - server.notify_client(entity, ServerMsg::private(s)); + server.notify_client(client, ServerMsg::private(s)); } else { server.notify_client( - entity, + client, ServerMsg::private(String::from("Not a pregenerated chunk.")), ); } } else { - server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); } } @@ -1048,12 +1200,18 @@ fn find_target( } } -fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_exp( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String); if let Some(exp) = a_exp { let ecs = server.state.ecs_mut(); - let target = find_target(&ecs, a_alias, entity); + let target = find_target(&ecs, a_alias, target); let mut error_msg = None; @@ -1071,17 +1229,23 @@ fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &Cha } if let Some(msg) = error_msg { - server.notify_client(entity, msg); + server.notify_client(client, msg); } } } -fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { +fn handle_level( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String); if let Some(lvl) = a_lvl { let ecs = server.state.ecs_mut(); - let target = find_target(&ecs, a_alias, entity); + let target = find_target(&ecs, a_alias, target); let mut error_msg = None; @@ -1104,19 +1268,25 @@ fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &C } if let Some(msg) = error_msg { - server.notify_client(entity, msg); + server.notify_client(client, msg); } } } use common::comp::Item; -fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { +fn handle_debug( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { if let Ok(items) = assets::load_glob::("common.items.debug.*") { server .state() .ecs() .write_storage::() - .get_mut(entity) + .get_mut(target) // TODO: Consider writing a `load_glob_cloned` in `assets` and using that here .map(|inv| inv.push_all_unique(items.iter().map(|item| item.as_ref().clone()))); let _ = server @@ -1124,12 +1294,12 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action: .ecs() .write_storage::() .insert( - entity, + target, comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), ); } else { server.notify_client( - entity, + client, ServerMsg::private(String::from( "Debug items not found? Something is very broken.", )), @@ -1139,12 +1309,13 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action: fn handle_remove_lights( server: &mut Server, - entity: EcsEntity, + client: EcsEntity, + target: EcsEntity, args: String, action: &ChatCommand, ) { let opt_radius = scan_fmt_some!(&args, action.arg_fmt, f32); - let opt_player_pos = server.state.read_component_cloned::(entity); + let opt_player_pos = server.state.read_component_cloned::(target); let mut to_delete = vec![]; match opt_player_pos { @@ -1168,7 +1339,7 @@ fn handle_remove_lights( } }, None => server.notify_client( - entity, + client, ServerMsg::private(String::from("You have no position.")), ), } @@ -1182,7 +1353,48 @@ fn handle_remove_lights( } server.notify_client( - entity, + client, ServerMsg::private(String::from(format!("Removed {} lights!", size))), ); } + +fn handle_sudo( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + args: String, + action: &ChatCommand, +) { + if let (Some(player_alias), Some(cmd), cmd_args) = + scan_fmt_some!(&args, action.arg_fmt, String, String, String) + { + let cmd_args = cmd_args.unwrap_or(String::from("")); + let cmd = if cmd.chars().next() == Some('/') { + cmd.chars().skip(1).collect() + } else { + cmd + }; + if let Some(action) = CHAT_COMMANDS.iter().find(|c| c.keyword == cmd) { + let ecs = server.state.ecs(); + let entity_opt = (&ecs.entities(), &ecs.read_storage::()) + .join() + .find(|(_, player)| player.alias == player_alias) + .map(|(entity, _)| entity); + if let Some(entity) = entity_opt { + (action.handler)(server, client, entity, cmd_args, action); + } else { + server.notify_client( + client, + ServerMsg::private(format!("Could not find that player")), + ); + } + } else { + server.notify_client( + client, + ServerMsg::private(format!("Unknown command: /{}", cmd)), + ); + } + } else { + server.notify_client(client, ServerMsg::private(String::from(action.help_string))); + } +} diff --git a/voxygen/src/anim/biped_large/mod.rs b/voxygen/src/anim/biped_large/mod.rs index ba73161fb7..34768b1a66 100644 --- a/voxygen/src/anim/biped_large/mod.rs +++ b/voxygen/src/anim/biped_large/mod.rs @@ -47,6 +47,8 @@ impl BipedLargeSkeleton { impl Skeleton for BipedLargeSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 11 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let upper_torso_mat = self.upper_torso.compute_base_matrix(); let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); diff --git a/voxygen/src/anim/bird_medium/mod.rs b/voxygen/src/anim/bird_medium/mod.rs index 64e2d400e3..2e3d3350c9 100644 --- a/voxygen/src/anim/bird_medium/mod.rs +++ b/voxygen/src/anim/bird_medium/mod.rs @@ -27,6 +27,8 @@ impl BirdMediumSkeleton { impl Skeleton for BirdMediumSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 7 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let torso_mat = self.torso.compute_base_matrix(); diff --git a/voxygen/src/anim/bird_small/mod.rs b/voxygen/src/anim/bird_small/mod.rs index 610b63122a..de36727d8d 100644 --- a/voxygen/src/anim/bird_small/mod.rs +++ b/voxygen/src/anim/bird_small/mod.rs @@ -31,6 +31,8 @@ impl BirdSmallSkeleton { impl Skeleton for BirdSmallSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 4 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let torso_mat = self.torso.compute_base_matrix(); diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index 9f50a3bd06..db01ba11ad 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -62,6 +62,8 @@ impl CharacterSkeleton { impl Skeleton for CharacterSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 15 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let chest_mat = self.chest.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix(); diff --git a/voxygen/src/anim/critter/mod.rs b/voxygen/src/anim/critter/mod.rs index 9677461239..bce32cf380 100644 --- a/voxygen/src/anim/critter/mod.rs +++ b/voxygen/src/anim/critter/mod.rs @@ -32,6 +32,8 @@ impl CritterSkeleton { impl Skeleton for CritterSkeleton { type Attr = CritterAttr; + fn bone_count(&self) -> usize { 5 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { [ FigureBoneData::new(self.head.compute_base_matrix()), diff --git a/voxygen/src/anim/dragon/mod.rs b/voxygen/src/anim/dragon/mod.rs index c3bfec7e61..86bd013b9b 100644 --- a/voxygen/src/anim/dragon/mod.rs +++ b/voxygen/src/anim/dragon/mod.rs @@ -49,6 +49,8 @@ impl DragonSkeleton { impl Skeleton for DragonSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 13 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let chest_front_mat = self.chest_front.compute_base_matrix(); let wing_in_l_mat = self.wing_in_l.compute_base_matrix(); diff --git a/voxygen/src/anim/fish_medium/mod.rs b/voxygen/src/anim/fish_medium/mod.rs index b085ed7220..f7f8519a2a 100644 --- a/voxygen/src/anim/fish_medium/mod.rs +++ b/voxygen/src/anim/fish_medium/mod.rs @@ -35,6 +35,8 @@ impl FishMediumSkeleton { impl Skeleton for FishMediumSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 6 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let torso_mat = self.torso.compute_base_matrix(); let rear_mat = self.rear.compute_base_matrix(); diff --git a/voxygen/src/anim/fish_small/mod.rs b/voxygen/src/anim/fish_small/mod.rs index ed26ef0247..1b3618b892 100644 --- a/voxygen/src/anim/fish_small/mod.rs +++ b/voxygen/src/anim/fish_small/mod.rs @@ -27,6 +27,8 @@ impl FishSmallSkeleton { impl Skeleton for FishSmallSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 2 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let torso_mat = self.torso.compute_base_matrix(); diff --git a/voxygen/src/anim/fixture/mod.rs b/voxygen/src/anim/fixture/mod.rs index 92a68d0125..b3426286df 100644 --- a/voxygen/src/anim/fixture/mod.rs +++ b/voxygen/src/anim/fixture/mod.rs @@ -13,9 +13,11 @@ impl FixtureSkeleton { impl Skeleton for FixtureSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 1 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { [ - FigureBoneData::new(vek::Mat4::identity()), + 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()), diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index de57de2c81..1e556aac9f 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -52,6 +52,8 @@ impl Bone { pub trait Skeleton: Send + Sync + 'static { type Attr; + fn bone_count(&self) -> usize { 16 } + fn compute_matrices(&self) -> [FigureBoneData; 16]; /// Change the current skeleton to be more like `target`. diff --git a/voxygen/src/anim/object/mod.rs b/voxygen/src/anim/object/mod.rs index d6f3ac510e..1b552d0485 100644 --- a/voxygen/src/anim/object/mod.rs +++ b/voxygen/src/anim/object/mod.rs @@ -15,24 +15,26 @@ const SCALE: f32 = 1.0 / 11.0; impl Skeleton for ObjectSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 1 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { [ FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), - 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()), ] } diff --git a/voxygen/src/anim/quadruped_medium/mod.rs b/voxygen/src/anim/quadruped_medium/mod.rs index 57f36c9c1f..a23e8f1793 100644 --- a/voxygen/src/anim/quadruped_medium/mod.rs +++ b/voxygen/src/anim/quadruped_medium/mod.rs @@ -31,6 +31,8 @@ impl QuadrupedMediumSkeleton { impl Skeleton for QuadrupedMediumSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 11 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { let ears_mat = self.ears.compute_base_matrix(); let head_upper_mat = self.head_upper.compute_base_matrix(); diff --git a/voxygen/src/anim/quadruped_small/mod.rs b/voxygen/src/anim/quadruped_small/mod.rs index fb4c559fbf..b043d01751 100644 --- a/voxygen/src/anim/quadruped_small/mod.rs +++ b/voxygen/src/anim/quadruped_small/mod.rs @@ -26,6 +26,8 @@ impl QuadrupedSmallSkeleton { impl Skeleton for QuadrupedSmallSkeleton { type Attr = SkeletonAttr; + fn bone_count(&self) -> usize { 6 } + fn compute_matrices(&self) -> [FigureBoneData; 16] { [ FigureBoneData::new(self.head.compute_base_matrix()), diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 9342d91470..cb16ed03b0 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -211,6 +211,7 @@ pub enum Event { ToggleSmoothPan(bool), AdjustViewDistance(u32), AdjustLodDetail(u32), + AdjustSpriteRenderDistance(u32), AdjustMusicVolume(f32), AdjustSfxVolume(f32), ChangeAudioDevice(String), @@ -1805,6 +1806,9 @@ impl Hud { settings_window::Event::AdjustLodDetail(lod_detail) => { events.push(Event::AdjustLodDetail(lod_detail)); }, + settings_window::Event::AdjustSpriteRenderDistance(view_distance) => { + events.push(Event::AdjustSpriteRenderDistance(view_distance)); + }, settings_window::Event::CrosshairTransp(crosshair_transp) => { events.push(Event::CrosshairTransp(crosshair_transp)); }, diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 688523c21f..2ecf3b15c8 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -92,6 +92,9 @@ widget_ids! { lod_detail_slider, lod_detail_text, lod_detail_value, + sprite_dist_slider, + sprite_dist_text, + sprite_dist_value, max_fps_slider, max_fps_text, max_fps_value, @@ -212,6 +215,7 @@ pub enum Event { ToggleMouseYInvert(bool), ToggleSmoothPan(bool), AdjustViewDistance(u32), + AdjustSpriteRenderDistance(u32), AdjustFOV(u16), AdjustLodDetail(u32), AdjustGamma(f32), @@ -1654,7 +1658,43 @@ impl<'a> Widget for SettingsWindow<'a> { .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.gamma_value, ui); + // Sprites VD + if let Some(new_val) = ImageSlider::discrete( + self.global_state.settings.graphics.sprite_render_distance, + 50, + 500, + self.imgs.slider_indicator, + self.imgs.slider, + ) + .w_h(104.0, 22.0) + .right_from(state.ids.vd_slider, 50.0) + .track_breadth(12.0) + .slider_length(10.0) + .pad_track((5.0, 5.0)) + .set(state.ids.sprite_dist_slider, ui) + { + events.push(Event::AdjustSpriteRenderDistance(new_val)); + } + Text::new( + &self + .localized_strings + .get("hud.settings.sprites_view_distance"), + ) + .up_from(state.ids.sprite_dist_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.sprite_dist_text, ui); + Text::new(&format!( + "{}", + self.global_state.settings.graphics.sprite_render_distance + )) + .right_from(state.ids.sprite_dist_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.sprite_dist_value, ui); // AaMode Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode")) .down_from(state.ids.gamma_slider, 8.0) diff --git a/voxygen/src/mesh/mod.rs b/voxygen/src/mesh/mod.rs index 86bd6cc55c..695c1e617b 100644 --- a/voxygen/src/mesh/mod.rs +++ b/voxygen/src/mesh/mod.rs @@ -4,14 +4,14 @@ mod vol; use crate::render::{self, Mesh}; -pub trait Meshable { +pub trait Meshable<'a, P: render::Pipeline, T: render::Pipeline> { type Pipeline: render::Pipeline; type TranslucentPipeline: render::Pipeline; type Supplement; // Generate meshes - one opaque, one translucent fn generate_mesh( - &self, + &'a self, supp: Self::Supplement, ) -> (Mesh, Mesh); } diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index 962a6ed93e..fff654f888 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -3,27 +3,43 @@ use crate::{ render::{self, FigurePipeline, Mesh, SpritePipeline}, }; use common::{ - figure::Segment, + figure::Cell, util::{linear_to_srgb, srgb_to_linear}, - vol::{IntoFullVolIterator, ReadVol, Vox}, + vol::{BaseVol, ReadVol, SizedVol, Vox}, }; use vek::*; type FigureVertex = ::Vertex; type SpriteVertex = ::Vertex; -impl Meshable for Segment { +impl<'a, V: 'a> Meshable<'a, FigurePipeline, FigurePipeline> 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; + type Supplement = (Vec3, Vec3); type TranslucentPipeline = FigurePipeline; fn generate_mesh( - &self, - offs: Self::Supplement, + &'a self, + (offs, scale): Self::Supplement, ) -> (Mesh, Mesh) { let mut mesh = Mesh::new(); - for (pos, vox) in self.full_vol_iter() { + 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()))); + + for (pos, vox) in vol_iter { if let Some(col) = vox.get_color() { vol::push_vox_verts( &mut mesh, @@ -32,7 +48,7 @@ impl Meshable for Segment { &[[[Rgba::from_opaque(col); 3]; 3]; 3], |origin, norm, col, light, ao| { FigureVertex::new( - origin, + origin * scale, norm, linear_to_srgb(srgb_to_linear(col) * light), ao, @@ -62,18 +78,34 @@ impl Meshable for Segment { } } -impl Meshable for Segment { +impl<'a, V: 'a> Meshable<'a, SpritePipeline, SpritePipeline> 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 = SpritePipeline; - type Supplement = Vec3; + type Supplement = (Vec3, Vec3); type TranslucentPipeline = SpritePipeline; fn generate_mesh( - &self, - offs: Self::Supplement, + &'a self, + (offs, scale): Self::Supplement, ) -> (Mesh, Mesh) { let mut mesh = Mesh::new(); - for (pos, vox) in self.full_vol_iter() { + 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()))); + + for (pos, vox) in vol_iter { if let Some(col) = vox.get_color() { vol::push_vox_verts( &mut mesh, @@ -82,7 +114,7 @@ impl Meshable for Segment { &[[[Rgba::from_opaque(col); 3]; 3]; 3], |origin, norm, col, light, ao| { SpriteVertex::new( - origin, + origin * scale, norm, linear_to_srgb(srgb_to_linear(col) * light), ao, diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index edec81f945..4bfd6510b8 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -198,15 +198,15 @@ fn calc_light + ReadVol + Debug>( } } -impl + ReadVol + Debug> Meshable - for VolGrid2d +impl<'a, V: RectRasterableVol + ReadVol + Debug> + Meshable<'a, TerrainPipeline, FluidPipeline> for VolGrid2d { type Pipeline = TerrainPipeline; type Supplement = Aabb; type TranslucentPipeline = FluidPipeline; fn generate_mesh( - &self, + &'a self, range: Self::Supplement, ) -> (Mesh, Mesh) { let mut light = calc_light(range, self); diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index fe12ec9c6b..b5ab23693b 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -11,11 +11,12 @@ use vek::*; gfx_defines! { vertex Vertex { - pos: [f32; 3] = "v_pos", - norm: [f32; 3] = "v_norm", - col: [f32; 3] = "v_col", - ao: f32 = "v_ao", - bone_idx: u8 = "v_bone_idx", + pos_norm: u32 = "v_pos_norm", + col: u32 = "v_col", + // BBBBBBAA + // B = Bone + // A = AO + ao_bone: u8 = "v_ao_bone", } constant Locals { @@ -49,17 +50,29 @@ gfx_defines! { impl Vertex { 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: pos.into_array(), - col: col.into_array(), - norm: norm.into_array(), - ao, - bone_idx, + pos_norm: pos + .map2(Vec3::new(0, 8, 16), |e, shift| { + ((e + 128.0) as u32) << shift + }) + .reduce_bitor() + | (norm_bits << 24), + 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.bone_idx = bone_idx; + self.ao_bone = (self.ao_bone & 0b11) | (bone_idx << 2); self } } diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 07df9826e8..ef790652b3 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -37,6 +37,7 @@ gfx_defines! { select_pos: [i32; 4] = "select_pos", gamma: [f32; 4] = "gamma", cam_mode: u32 = "cam_mode", + sprite_render_distance: f32 = "sprite_render_distance", } constant Light { @@ -67,6 +68,7 @@ impl Globals { select_pos: Option>, gamma: f32, cam_mode: CameraMode, + sprite_render_distance: f32, ) -> Self { Self { view_mat: arr_to_mat(view_mat.into_col_array()), @@ -86,6 +88,7 @@ impl Globals { .into_array(), gamma: [gamma; 4], cam_mode: cam_mode as u32, + sprite_render_distance, } } } @@ -108,6 +111,7 @@ impl Default for Globals { None, 1.0, CameraMode::ThirdPerson, + 250.0, ) } } diff --git a/voxygen/src/render/pipelines/sprite.rs b/voxygen/src/render/pipelines/sprite.rs index e77d9f9e01..f30304eaf4 100644 --- a/voxygen/src/render/pipelines/sprite.rs +++ b/voxygen/src/render/pipelines/sprite.rs @@ -12,9 +12,12 @@ use vek::*; gfx_defines! { vertex Vertex { pos: [f32; 3] = "v_pos", - norm: [f32; 3] = "v_norm", - col: [f32; 3] = "v_col", - ao: f32 = "v_ao", + // ____BBBBBBBBGGGGGGGGRRRRRRRR + col: u32 = "v_col", + // ...AANNN + // A = AO + // N = Normal + norm_ao: u32 = "v_norm_ao", } vertex Instance { @@ -46,11 +49,20 @@ gfx_defines! { impl Vertex { pub fn new(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 { + if norm.y < 0.0 { 2 } else { 3 } + } else { + if norm.z < 0.0 { 4 } else { 5 } + }; + Self { pos: pos.into_array(), - col: col.into_array(), - norm: norm.into_array(), - ao, + 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), } } } diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 7903b30d3c..67f4830554 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -1,6 +1,7 @@ use super::load::*; use crate::{ anim::{self, Skeleton}, + mesh::Meshable, render::{FigurePipeline, Mesh, Model, Renderer}, scene::camera::CameraMode, }; @@ -10,12 +11,15 @@ use common::{ item::{tool::ToolKind, ItemKind}, Body, CharacterState, Item, Loadout, }, + figure::Segment, + vol::BaseVol, }; use hashbrown::{hash_map::Entry, HashMap}; use std::{ convert::TryInto, mem::{discriminant, Discriminant}, }; +use vek::*; #[derive(PartialEq, Eq, Hash, Clone)] enum FigureKey { @@ -64,7 +68,7 @@ pub struct FigureModelCache where Skel: Skeleton, { - models: HashMap, Skel::Attr), u64)>, + models: HashMap; 3], Skel::Attr), u64)>, manifest_indicator: ReloadIndicator, } @@ -76,6 +80,522 @@ impl FigureModelCache { } } + fn bone_meshes( + body: Body, + loadout: Option<&Loadout>, + character_state: Option<&CharacterState>, + camera_mode: CameraMode, + manifest_indicator: &mut ReloadIndicator, + generate_mesh: fn(&Segment, Vec3) -> Mesh, + ) -> [Option>; 16] { + match body { + Body::Humanoid(body) => { + let humanoid_head_spec = HumHeadSpec::load_watched(manifest_indicator); + let humanoid_armor_shoulder_spec = + HumArmorShoulderSpec::load_watched(manifest_indicator); + let humanoid_armor_chest_spec = HumArmorChestSpec::load_watched(manifest_indicator); + let humanoid_armor_hand_spec = HumArmorHandSpec::load_watched(manifest_indicator); + let humanoid_armor_belt_spec = HumArmorBeltSpec::load_watched(manifest_indicator); + let humanoid_armor_back_spec = HumArmorBackSpec::load_watched(manifest_indicator); + let humanoid_armor_lantern_spec = + HumArmorLanternSpec::load_watched(manifest_indicator); + let humanoid_armor_pants_spec = HumArmorPantsSpec::load_watched(manifest_indicator); + let humanoid_armor_foot_spec = HumArmorFootSpec::load_watched(manifest_indicator); + let humanoid_main_weapon_spec = HumMainWeaponSpec::load_watched(manifest_indicator); + + // TODO: This is bad code, maybe this method should return Option<_> + let default_loadout = Loadout::default(); + let loadout = loadout.unwrap_or(&default_loadout); + + [ + match camera_mode { + CameraMode::ThirdPerson => Some(humanoid_head_spec.mesh_head( + body.race, + body.body_type, + body.hair_color, + body.hair_style, + body.beard, + body.eye_color, + body.skin, + body.eyebrows, + body.accessory, + generate_mesh, + )), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(humanoid_armor_chest_spec.mesh_chest( + &body, + loadout, + generate_mesh, + )), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => { + Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout, generate_mesh)) + }, + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => { + Some(humanoid_armor_back_spec.mesh_back(&body, loadout, generate_mesh)) + }, + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(humanoid_armor_pants_spec.mesh_pants( + &body, + loadout, + generate_mesh, + )), + CameraMode::FirstPerson => None, + }, + if camera_mode == CameraMode::FirstPerson + && character_state.map(|cs| cs.is_dodge()).unwrap_or_default() + { + None + } else { + Some(humanoid_armor_hand_spec.mesh_left_hand(&body, loadout, generate_mesh)) + }, + if character_state.map(|cs| cs.is_dodge()).unwrap_or_default() { + None + } else { + Some(humanoid_armor_hand_spec.mesh_right_hand( + &body, + loadout, + generate_mesh, + )) + }, + match camera_mode { + CameraMode::ThirdPerson => Some(humanoid_armor_foot_spec.mesh_left_foot( + &body, + loadout, + generate_mesh, + )), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => Some(humanoid_armor_foot_spec.mesh_right_foot( + &body, + loadout, + generate_mesh, + )), + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => { + Some(humanoid_armor_shoulder_spec.mesh_left_shoulder( + &body, + loadout, + generate_mesh, + )) + }, + CameraMode::FirstPerson => None, + }, + match camera_mode { + CameraMode::ThirdPerson => { + 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), + generate_mesh, + )) + } else { + None + }, + None, + Some(humanoid_armor_lantern_spec.mesh_lantern(&body, loadout, generate_mesh)), + None, + ] + }, + Body::QuadrupedSmall(body) => { + let quadruped_small_central_spec = + QuadrupedSmallCentralSpec::load_watched(manifest_indicator); + let quadruped_small_lateral_spec = + QuadrupedSmallLateralSpec::load_watched(manifest_indicator); + + [ + Some(quadruped_small_central_spec.mesh_head( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_small_central_spec.mesh_chest( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_small_lateral_spec.mesh_foot_lf( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_small_lateral_spec.mesh_foot_rf( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_small_lateral_spec.mesh_foot_lb( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_small_lateral_spec.mesh_foot_rb( + body.species, + body.body_type, + generate_mesh, + )), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ] + }, + Body::QuadrupedMedium(body) => { + let quadruped_medium_central_spec = + QuadrupedMediumCentralSpec::load_watched(manifest_indicator); + let quadruped_medium_lateral_spec = + QuadrupedMediumLateralSpec::load_watched(manifest_indicator); + + [ + Some(quadruped_medium_central_spec.mesh_head_upper( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_central_spec.mesh_head_lower( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_central_spec.mesh_jaw( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_central_spec.mesh_tail( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_central_spec.mesh_torso_f( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_central_spec.mesh_torso_b( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_central_spec.mesh_ears( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_lateral_spec.mesh_foot_lf( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_lateral_spec.mesh_foot_rf( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_lateral_spec.mesh_foot_lb( + body.species, + body.body_type, + generate_mesh, + )), + Some(quadruped_medium_lateral_spec.mesh_foot_rb( + body.species, + body.body_type, + generate_mesh, + )), + None, + None, + None, + None, + None, + ] + }, + Body::BirdMedium(body) => { + let bird_medium_center_spec = + BirdMediumCenterSpec::load_watched(manifest_indicator); + let bird_medium_lateral_spec = + BirdMediumLateralSpec::load_watched(manifest_indicator); + + [ + Some(bird_medium_center_spec.mesh_head( + body.species, + body.body_type, + generate_mesh, + )), + Some(bird_medium_center_spec.mesh_torso( + body.species, + body.body_type, + generate_mesh, + )), + Some(bird_medium_center_spec.mesh_tail( + body.species, + body.body_type, + generate_mesh, + )), + Some(bird_medium_lateral_spec.mesh_wing_l( + body.species, + body.body_type, + generate_mesh, + )), + Some(bird_medium_lateral_spec.mesh_wing_r( + body.species, + body.body_type, + generate_mesh, + )), + Some(bird_medium_lateral_spec.mesh_foot_l( + body.species, + body.body_type, + generate_mesh, + )), + Some(bird_medium_lateral_spec.mesh_foot_r( + body.species, + body.body_type, + generate_mesh, + )), + None, + None, + None, + None, + None, + None, + None, + None, + None, + ] + }, + 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)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ], + Body::Dragon(body) => [ + Some(mesh_dragon_head(body.head, generate_mesh)), + Some(mesh_dragon_chest_front(body.chest_front, generate_mesh)), + Some(mesh_dragon_chest_rear(body.chest_rear, generate_mesh)), + Some(mesh_dragon_tail_front(body.tail_front, generate_mesh)), + Some(mesh_dragon_tail_rear(body.tail_rear, generate_mesh)), + Some(mesh_dragon_wing_in_l(body.wing_in_l, generate_mesh)), + Some(mesh_dragon_wing_in_r(body.wing_in_r, generate_mesh)), + Some(mesh_dragon_wing_out_l(body.wing_out_l, generate_mesh)), + Some(mesh_dragon_wing_out_r(body.wing_out_r, generate_mesh)), + Some(mesh_dragon_foot_fl(body.foot_fl, generate_mesh)), + Some(mesh_dragon_foot_fr(body.foot_fr, generate_mesh)), + Some(mesh_dragon_foot_bl(body.foot_bl, generate_mesh)), + Some(mesh_dragon_foot_br(body.foot_br, generate_mesh)), + None, + None, + 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)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ], + Body::FishSmall(body) => [ + Some(mesh_fish_small_torso(body.torso, generate_mesh)), + Some(mesh_fish_small_tail(body.tail, generate_mesh)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ], + Body::BipedLarge(body) => { + let biped_large_center_spec = + BipedLargeCenterSpec::load_watched(manifest_indicator); + let biped_large_lateral_spec = + BipedLargeLateralSpec::load_watched(manifest_indicator); + + [ + Some(biped_large_center_spec.mesh_head( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_center_spec.mesh_torso_upper( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_center_spec.mesh_torso_lower( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_shoulder_l( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_shoulder_r( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_hand_l( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_hand_r( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_leg_l( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_leg_r( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_foot_l( + body.species, + body.body_type, + generate_mesh, + )), + Some(biped_large_lateral_spec.mesh_foot_r( + body.species, + body.body_type, + generate_mesh, + )), + None, + None, + None, + None, + None, + ] + }, + Body::Critter(body) => { + let critter_center_spec = CritterCenterSpec::load_watched(manifest_indicator); + + [ + Some(critter_center_spec.mesh_head( + body.species, + body.body_type, + generate_mesh, + )), + Some(critter_center_spec.mesh_chest( + body.species, + body.body_type, + generate_mesh, + )), + Some(critter_center_spec.mesh_feet_f( + body.species, + body.body_type, + generate_mesh, + )), + Some(critter_center_spec.mesh_feet_b( + body.species, + body.body_type, + generate_mesh, + )), + Some(critter_center_spec.mesh_tail( + body.species, + body.body_type, + generate_mesh, + )), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ] + }, + Body::Object(object) => [ + Some(mesh_object(object, generate_mesh)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ], + } + } + pub fn get_or_create_model( &mut self, renderer: &mut Renderer, @@ -84,7 +604,7 @@ impl FigureModelCache { tick: u64, camera_mode: CameraMode, character_state: Option<&CharacterState>, - ) -> &(Model, Skel::Attr) + ) -> &([Model; 3], Skel::Attr) where for<'a> &'a common::comp::Body: std::convert::TryInto, Skel::Attr: Default, @@ -108,501 +628,74 @@ impl FigureModelCache { Entry::Vacant(v) => { &v.insert(( { - let bone_meshes = match body { - Body::Humanoid(body) => { - let humanoid_head_spec = - HumHeadSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_shoulder_spec = - HumArmorShoulderSpec::load_watched( - &mut self.manifest_indicator, - ); - let humanoid_armor_chest_spec = - HumArmorChestSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_hand_spec = - HumArmorHandSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_belt_spec = - HumArmorBeltSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_back_spec = - HumArmorBackSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_lantern_spec = - HumArmorLanternSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_pants_spec = - HumArmorPantsSpec::load_watched(&mut self.manifest_indicator); - let humanoid_armor_foot_spec = - HumArmorFootSpec::load_watched(&mut self.manifest_indicator); - let humanoid_main_weapon_spec = - HumMainWeaponSpec::load_watched(&mut self.manifest_indicator); - - // TODO: This is bad code, maybe this method should return Option<_> - let default_loadout = Loadout::default(); - let loadout = loadout.unwrap_or(&default_loadout); - - [ - match camera_mode { - CameraMode::ThirdPerson => { - Some(humanoid_head_spec.mesh_head( - body.race, - body.body_type, - body.hair_color, - body.hair_style, - body.beard, - body.eye_color, - body.skin, - body.eyebrows, - body.accessory, - )) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => Some( - humanoid_armor_chest_spec.mesh_chest(&body, loadout), - ), - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => { - Some(humanoid_armor_belt_spec.mesh_belt(&body, loadout)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => { - Some(humanoid_armor_back_spec.mesh_back(&body, loadout)) - }, - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => Some( - humanoid_armor_pants_spec.mesh_pants(&body, loadout), - ), - CameraMode::FirstPerson => None, - }, - if camera_mode == CameraMode::FirstPerson - && character_state - .map(|cs| cs.is_dodge()) - .unwrap_or_default() - { - None - } else { - Some( - humanoid_armor_hand_spec.mesh_left_hand(&body, loadout), - ) - }, - if character_state.map(|cs| cs.is_dodge()).unwrap_or_default() { - None - } else { - Some( - humanoid_armor_hand_spec - .mesh_right_hand(&body, loadout), - ) - }, - match camera_mode { - CameraMode::ThirdPerson => Some( - humanoid_armor_foot_spec.mesh_left_foot(&body, loadout), - ), - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => Some( - humanoid_armor_foot_spec - .mesh_right_foot(&body, loadout), - ), - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => Some( - humanoid_armor_shoulder_spec - .mesh_left_shoulder(&body, loadout), - ), - CameraMode::FirstPerson => None, - }, - match camera_mode { - CameraMode::ThirdPerson => Some( - humanoid_armor_shoulder_spec - .mesh_right_shoulder(&body, loadout), - ), - CameraMode::FirstPerson => None, - }, - Some(mesh_glider()), - 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), - )) - } else { - None - }, - None, - Some(humanoid_armor_lantern_spec.mesh_lantern(&body, loadout)), - None, - ] - }, - Body::QuadrupedSmall(body) => { - let quadruped_small_central_spec = - QuadrupedSmallCentralSpec::load_watched( - &mut self.manifest_indicator, - ); - let quadruped_small_lateral_spec = - QuadrupedSmallLateralSpec::load_watched( - &mut self.manifest_indicator, - ); - - [ - Some( - quadruped_small_central_spec - .mesh_head(body.species, body.body_type), - ), - Some( - quadruped_small_central_spec - .mesh_chest(body.species, body.body_type), - ), - Some( - quadruped_small_lateral_spec - .mesh_foot_lf(body.species, body.body_type), - ), - Some( - quadruped_small_lateral_spec - .mesh_foot_rf(body.species, body.body_type), - ), - Some( - quadruped_small_lateral_spec - .mesh_foot_lb(body.species, body.body_type), - ), - Some( - quadruped_small_lateral_spec - .mesh_foot_rb(body.species, body.body_type), - ), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ] - }, - Body::QuadrupedMedium(body) => { - let quadruped_medium_central_spec = - QuadrupedMediumCentralSpec::load_watched( - &mut self.manifest_indicator, - ); - let quadruped_medium_lateral_spec = - QuadrupedMediumLateralSpec::load_watched( - &mut self.manifest_indicator, - ); - - [ - Some( - quadruped_medium_central_spec - .mesh_head_upper(body.species, body.body_type), - ), - Some( - quadruped_medium_central_spec - .mesh_head_lower(body.species, body.body_type), - ), - Some( - quadruped_medium_central_spec - .mesh_jaw(body.species, body.body_type), - ), - Some( - quadruped_medium_central_spec - .mesh_tail(body.species, body.body_type), - ), - Some( - quadruped_medium_central_spec - .mesh_torso_f(body.species, body.body_type), - ), - Some( - quadruped_medium_central_spec - .mesh_torso_b(body.species, body.body_type), - ), - Some( - quadruped_medium_central_spec - .mesh_ears(body.species, body.body_type), - ), - Some( - quadruped_medium_lateral_spec - .mesh_foot_lf(body.species, body.body_type), - ), - Some( - quadruped_medium_lateral_spec - .mesh_foot_rf(body.species, body.body_type), - ), - Some( - quadruped_medium_lateral_spec - .mesh_foot_lb(body.species, body.body_type), - ), - Some( - quadruped_medium_lateral_spec - .mesh_foot_rb(body.species, body.body_type), - ), - None, - None, - None, - None, - None, - ] - }, - Body::BirdMedium(body) => { - let bird_medium_center_spec = BirdMediumCenterSpec::load_watched( - &mut self.manifest_indicator, - ); - let bird_medium_lateral_spec = BirdMediumLateralSpec::load_watched( - &mut self.manifest_indicator, - ); - - [ - Some( - bird_medium_center_spec - .mesh_head(body.species, body.body_type), - ), - Some( - bird_medium_center_spec - .mesh_torso(body.species, body.body_type), - ), - Some( - bird_medium_center_spec - .mesh_tail(body.species, body.body_type), - ), - Some( - bird_medium_lateral_spec - .mesh_wing_l(body.species, body.body_type), - ), - Some( - bird_medium_lateral_spec - .mesh_wing_r(body.species, body.body_type), - ), - Some( - bird_medium_lateral_spec - .mesh_foot_l(body.species, body.body_type), - ), - Some( - bird_medium_lateral_spec - .mesh_foot_r(body.species, body.body_type), - ), - None, - None, - None, - None, - None, - None, - None, - None, - None, - ] - }, - Body::FishMedium(body) => [ - Some(mesh_fish_medium_head(body.head)), - Some(mesh_fish_medium_torso(body.torso)), - Some(mesh_fish_medium_rear(body.rear)), - Some(mesh_fish_medium_tail(body.tail)), - Some(mesh_fish_medium_fin_l(body.fin_l)), - Some(mesh_fish_medium_fin_r(body.fin_r)), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ], - Body::Dragon(body) => [ - Some(mesh_dragon_head(body.head)), - Some(mesh_dragon_chest_front(body.chest_front)), - Some(mesh_dragon_chest_rear(body.chest_rear)), - Some(mesh_dragon_tail_front(body.tail_front)), - Some(mesh_dragon_tail_rear(body.tail_rear)), - Some(mesh_dragon_wing_in_l(body.wing_in_l)), - Some(mesh_dragon_wing_in_r(body.wing_in_r)), - Some(mesh_dragon_wing_out_l(body.wing_out_l)), - Some(mesh_dragon_wing_out_r(body.wing_out_r)), - Some(mesh_dragon_foot_fl(body.foot_fl)), - Some(mesh_dragon_foot_fr(body.foot_fr)), - Some(mesh_dragon_foot_bl(body.foot_bl)), - Some(mesh_dragon_foot_br(body.foot_br)), - None, - None, - None, - ], - Body::BirdSmall(body) => [ - Some(mesh_bird_small_head(body.head)), - Some(mesh_bird_small_torso(body.torso)), - Some(mesh_bird_small_wing_l(body.wing_l)), - Some(mesh_bird_small_wing_r(body.wing_r)), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ], - Body::FishSmall(body) => [ - Some(mesh_fish_small_torso(body.torso)), - Some(mesh_fish_small_tail(body.tail)), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ], - Body::BipedLarge(body) => { - let biped_large_center_spec = BipedLargeCenterSpec::load_watched( - &mut self.manifest_indicator, - ); - let biped_large_lateral_spec = BipedLargeLateralSpec::load_watched( - &mut self.manifest_indicator, - ); - - [ - Some( - biped_large_center_spec - .mesh_head(body.species, body.body_type), - ), - Some( - biped_large_center_spec - .mesh_torso_upper(body.species, body.body_type), - ), - Some( - biped_large_center_spec - .mesh_torso_lower(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_shoulder_l(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_shoulder_r(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_hand_l(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_hand_r(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_leg_l(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_leg_r(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_foot_l(body.species, body.body_type), - ), - Some( - biped_large_lateral_spec - .mesh_foot_r(body.species, body.body_type), - ), - None, - None, - None, - None, - None, - ] - }, - Body::Critter(body) => { - let critter_center_spec = - CritterCenterSpec::load_watched(&mut self.manifest_indicator); - - [ - Some( - critter_center_spec.mesh_head(body.species, body.body_type), - ), - Some( - critter_center_spec - .mesh_chest(body.species, body.body_type), - ), - Some( - critter_center_spec - .mesh_feet_f(body.species, body.body_type), - ), - Some( - critter_center_spec - .mesh_feet_b(body.species, body.body_type), - ), - Some( - critter_center_spec.mesh_tail(body.species, body.body_type), - ), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ] - }, - Body::Object(object) => [ - Some(mesh_object(object)), - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - ], - }; - let skeleton_attr = (&body) .try_into() .ok() .unwrap_or_else(::default); - let mut mesh = Mesh::new(); - bone_meshes + 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, + ) .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)) }); + renderer.create_model(&mesh).unwrap() + }; - (renderer.create_model(&mesh).unwrap(), skeleton_attr) + fn generate_mesh( + segment: &Segment, + offset: Vec3, + ) -> Mesh { + Meshable::::generate_mesh( + segment, + (offset, Vec3::one()), + ) + .0 + } + + 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_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 + } + + ( + [ + make_model(generate_mesh), + make_model(generate_mesh_lod_mid), + make_model(generate_mesh_lod_low), + ], + skeleton_attr, + ) }, tick, )) diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index 5f9762bcf3..904ccfbb43 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -1,7 +1,4 @@ -use crate::{ - mesh::Meshable, - render::{FigurePipeline, Mesh}, -}; +use crate::render::{FigurePipeline, Mesh}; use common::{ assets::{self, watch::ReloadIndicator, Asset}, comp::{ @@ -54,11 +51,11 @@ fn graceful_load_mat_segment_flipped(mesh_name: &str) -> MatSegment { MatSegment::from_vox(graceful_load_vox(mesh_name).as_ref(), true) } -fn generate_mesh(segment: &Segment, offset: Vec3) -> Mesh { - Meshable::::generate_mesh(segment, offset).0 -} - -pub fn load_mesh(mesh_name: &str, position: Vec3) -> Mesh { +pub fn load_mesh( + mesh_name: &str, + position: Vec3, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { generate_mesh(&load_segment(mesh_name), position) } @@ -159,6 +156,7 @@ impl HumHeadSpec { skin: u8, _eyebrows: Eyebrows, accessory: u8, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(race, body_type)) { Some(spec) => spec, @@ -167,7 +165,7 @@ impl HumHeadSpec { "No head specification exists for the combination of {:?} and {:?}", race, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; @@ -349,7 +347,13 @@ impl HumArmorShoulderSpec { .unwrap() } - fn mesh_shoulder(&self, body: &Body, loadout: &Loadout, flipped: bool) -> Mesh { + fn mesh_shoulder( + &self, + body: &Body, + loadout: &Loadout, + flipped: bool, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Shoulder(shoulder), .. @@ -359,7 +363,7 @@ impl HumArmorShoulderSpec { Some(spec) => spec, None => { error!("No shoulder specification exists for {:?}", shoulder); - return load_mesh("not_found", Vec3::new(-3.0, -3.5, 0.1)); + return load_mesh("not_found", Vec3::new(-3.0, -3.5, 0.1), generate_mesh); }, } } else { @@ -391,12 +395,22 @@ impl HumArmorShoulderSpec { generate_mesh(&shoulder_segment, Vec3::from(offset)) } - pub fn mesh_left_shoulder(&self, body: &Body, loadout: &Loadout) -> Mesh { - self.mesh_shoulder(body, loadout, true) + 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) } - pub fn mesh_right_shoulder(&self, body: &Body, loadout: &Loadout) -> Mesh { - self.mesh_shoulder(body, loadout, false) + 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) } } // Chest @@ -406,7 +420,12 @@ impl HumArmorChestSpec { .unwrap() } - pub fn mesh_chest(&self, body: &Body, loadout: &Loadout) -> Mesh { + pub fn mesh_chest( + &self, + body: &Body, + loadout: &Loadout, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Chest(chest), .. @@ -416,7 +435,7 @@ impl HumArmorChestSpec { Some(spec) => spec, None => { error!("No chest specification exists for {:?}", loadout.chest); - return load_mesh("not_found", Vec3::new(-7.0, -3.5, 2.0)); + return load_mesh("not_found", Vec3::new(-7.0, -3.5, 2.0), generate_mesh); }, } } else { @@ -457,7 +476,13 @@ impl HumArmorHandSpec { .unwrap() } - fn mesh_hand(&self, body: &Body, loadout: &Loadout, flipped: bool) -> Mesh { + fn mesh_hand( + &self, + body: &Body, + loadout: &Loadout, + flipped: bool, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Hand(hand), .. @@ -467,7 +492,7 @@ impl HumArmorHandSpec { Some(spec) => spec, None => { error!("No hand specification exists for {:?}", hand); - return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0)); + return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0), generate_mesh); }, } } else { @@ -494,12 +519,22 @@ impl HumArmorHandSpec { generate_mesh(&hand_segment, Vec3::from(offset)) } - pub fn mesh_left_hand(&self, body: &Body, loadout: &Loadout) -> Mesh { - self.mesh_hand(body, loadout, true) + 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) } - pub fn mesh_right_hand(&self, body: &Body, loadout: &Loadout) -> Mesh { - self.mesh_hand(body, loadout, false) + 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) } } // Belt @@ -509,7 +544,12 @@ impl HumArmorBeltSpec { .unwrap() } - pub fn mesh_belt(&self, body: &Body, loadout: &Loadout) -> Mesh { + pub fn mesh_belt( + &self, + body: &Body, + loadout: &Loadout, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Belt(belt), .. @@ -519,7 +559,7 @@ impl HumArmorBeltSpec { Some(spec) => spec, None => { error!("No belt specification exists for {:?}", belt); - return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0)); + return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0), generate_mesh); }, } } else { @@ -543,7 +583,12 @@ impl HumArmorBackSpec { .unwrap() } - pub fn mesh_back(&self, body: &Body, loadout: &Loadout) -> Mesh { + pub fn mesh_back( + &self, + body: &Body, + loadout: &Loadout, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Back(back), .. @@ -553,7 +598,7 @@ impl HumArmorBackSpec { Some(spec) => spec, None => { error!("No back specification exists for {:?}", back); - return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0)); + return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0), generate_mesh); }, } } else { @@ -577,7 +622,12 @@ impl HumArmorPantsSpec { .unwrap() } - pub fn mesh_pants(&self, body: &Body, loadout: &Loadout) -> Mesh { + pub fn mesh_pants( + &self, + body: &Body, + loadout: &Loadout, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Pants(pants), .. @@ -587,7 +637,7 @@ impl HumArmorPantsSpec { Some(spec) => spec, None => { error!("No pants specification exists for {:?}", pants); - return load_mesh("not_found", Vec3::new(-5.0, -3.5, 1.0)); + return load_mesh("not_found", Vec3::new(-5.0, -3.5, 1.0), generate_mesh); }, } } else { @@ -628,7 +678,13 @@ impl HumArmorFootSpec { .unwrap() } - fn mesh_foot(&self, body: &Body, loadout: &Loadout, flipped: bool) -> Mesh { + fn mesh_foot( + &self, + body: &Body, + loadout: &Loadout, + flipped: bool, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Foot(foot), .. @@ -638,7 +694,7 @@ impl HumArmorFootSpec { Some(spec) => spec, None => { error!("No foot specification exists for {:?}", foot); - return load_mesh("not_found", Vec3::new(-2.5, -3.5, -9.0)); + return load_mesh("not_found", Vec3::new(-2.5, -3.5, -9.0), generate_mesh); }, } } else { @@ -659,12 +715,22 @@ impl HumArmorFootSpec { generate_mesh(&foot_segment, Vec3::from(spec.vox_spec.1)) } - pub fn mesh_left_foot(&self, body: &Body, loadout: &Loadout) -> Mesh { - self.mesh_foot(body, loadout, true) + 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) } - pub fn mesh_right_foot(&self, body: &Body, loadout: &Loadout) -> Mesh { - self.mesh_foot(body, loadout, false) + 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) } } @@ -674,7 +740,11 @@ impl HumMainWeaponSpec { .unwrap() } - pub fn mesh_main_weapon(&self, item_kind: Option<&ItemKind>) -> Mesh { + pub fn mesh_main_weapon( + &self, + item_kind: Option<&ItemKind>, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let tool_kind = if let Some(ItemKind::Tool(Tool { kind, .. })) = item_kind { kind } else { @@ -685,7 +755,7 @@ impl HumMainWeaponSpec { Some(spec) => spec, None => { error!("No hand specification exists for {:?}", tool_kind); - return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0)); + return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0), generate_mesh); }, }; @@ -699,14 +769,19 @@ impl HumArmorLanternSpec { assets::load_watched::("voxygen.voxel.humanoid_lantern_manifest", indicator).unwrap() } - pub fn mesh_lantern(&self, body: &Body, loadout: &Loadout) -> Mesh { + 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)) = loadout.lantern.as_ref().map(|i| &i.kind) { match self.0.map.get(&lantern) { Some(spec) => spec, None => { error!("No lantern specification exists for {:?}", lantern); - return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0)); + return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0), generate_mesh); }, } } else { @@ -729,7 +804,12 @@ impl HumArmorHeadSpec { .unwrap() } - pub fn mesh_head(&self, body: &Body, loadout: &Loadout) -> Mesh { + pub fn mesh_head( + &self, + body: &Body, + loadout: &Loadout, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Head(head), .. @@ -739,7 +819,7 @@ impl HumArmorHeadSpec { Some(spec) => spec, None => { error!("No head specification exists for {:?}", head); - return load_mesh("not_found", Vec3::new(-5.0, -3.5, 1.0)); + return load_mesh("not_found", Vec3::new(-5.0, -3.5, 1.0), generate_mesh); }, } } else { @@ -779,7 +859,12 @@ impl HumArmorTabardSpec { .unwrap() } - pub fn mesh_tabard(&self, body: &Body, loadout: &Loadout) -> Mesh { + pub fn mesh_tabard( + &self, + body: &Body, + loadout: &Loadout, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = if let Some(ItemKind::Armor { kind: Armor::Tabard(tabard), .. @@ -789,7 +874,7 @@ impl HumArmorTabardSpec { Some(spec) => spec, None => { error!("No tabard specification exists for {:?}", tabard); - return load_mesh("not_found", Vec3::new(-5.0, -3.5, 1.0)); + return load_mesh("not_found", Vec3::new(-5.0, -3.5, 1.0), generate_mesh); }, } } else { @@ -824,8 +909,14 @@ impl HumArmorTabardSpec { } } // TODO: Inventory -pub fn mesh_glider() -> Mesh { - load_mesh("object.glider", Vec3::new(-26.0, -26.0, -5.0)) +pub fn mesh_glider( + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { + load_mesh( + "object.glider", + Vec3::new(-26.0, -26.0, -5.0), + generate_mesh, + ) } ///////// @@ -881,7 +972,12 @@ impl QuadrupedSmallCentralSpec { .unwrap() } - pub fn mesh_head(&self, species: QSSpecies, body_type: QSBodyType) -> Mesh { + pub fn mesh_head( + &self, + species: QSSpecies, + body_type: QSBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -889,7 +985,7 @@ impl QuadrupedSmallCentralSpec { "No head specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.head.central.0); @@ -897,7 +993,12 @@ impl QuadrupedSmallCentralSpec { generate_mesh(¢ral, Vec3::from(spec.head.offset)) } - pub fn mesh_chest(&self, species: QSSpecies, body_type: QSBodyType) -> Mesh { + pub fn mesh_chest( + &self, + species: QSSpecies, + body_type: QSBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -905,7 +1006,7 @@ impl QuadrupedSmallCentralSpec { "No chest specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.chest.central.0); @@ -920,7 +1021,12 @@ impl QuadrupedSmallLateralSpec { .unwrap() } - pub fn mesh_foot_lf(&self, species: QSSpecies, body_type: QSBodyType) -> Mesh { + pub fn mesh_foot_lf( + &self, + species: QSSpecies, + body_type: QSBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -928,7 +1034,7 @@ impl QuadrupedSmallLateralSpec { "No leg specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.left_front.lateral.0); @@ -936,7 +1042,12 @@ impl QuadrupedSmallLateralSpec { generate_mesh(&lateral, Vec3::from(spec.left_front.offset)) } - pub fn mesh_foot_rf(&self, species: QSSpecies, body_type: QSBodyType) -> Mesh { + pub fn mesh_foot_rf( + &self, + species: QSSpecies, + body_type: QSBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -944,7 +1055,7 @@ impl QuadrupedSmallLateralSpec { "No leg specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.right_front.lateral.0); @@ -952,7 +1063,12 @@ impl QuadrupedSmallLateralSpec { generate_mesh(&lateral, Vec3::from(spec.right_front.offset)) } - pub fn mesh_foot_lb(&self, species: QSSpecies, body_type: QSBodyType) -> Mesh { + pub fn mesh_foot_lb( + &self, + species: QSSpecies, + body_type: QSBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -960,7 +1076,7 @@ impl QuadrupedSmallLateralSpec { "No leg specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.left_back.lateral.0); @@ -968,7 +1084,12 @@ impl QuadrupedSmallLateralSpec { generate_mesh(&lateral, Vec3::from(spec.left_back.offset)) } - pub fn mesh_foot_rb(&self, species: QSSpecies, body_type: QSBodyType) -> Mesh { + pub fn mesh_foot_rb( + &self, + species: QSSpecies, + body_type: QSBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -976,7 +1097,7 @@ impl QuadrupedSmallLateralSpec { "No leg specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.right_back.lateral.0); @@ -1046,6 +1167,7 @@ impl QuadrupedMediumCentralSpec { &self, species: QMSpecies, body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, @@ -1054,7 +1176,7 @@ impl QuadrupedMediumCentralSpec { "No upper head specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.upper.central.0); @@ -1066,6 +1188,7 @@ impl QuadrupedMediumCentralSpec { &self, species: QMSpecies, body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, @@ -1074,7 +1197,7 @@ impl QuadrupedMediumCentralSpec { "No lower head specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.lower.central.0); @@ -1082,7 +1205,12 @@ impl QuadrupedMediumCentralSpec { generate_mesh(¢ral, Vec3::from(spec.lower.offset)) } - pub fn mesh_jaw(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_jaw( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1090,7 +1218,7 @@ impl QuadrupedMediumCentralSpec { "No jaw specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.jaw.central.0); @@ -1098,7 +1226,12 @@ impl QuadrupedMediumCentralSpec { generate_mesh(¢ral, Vec3::from(spec.jaw.offset)) } - pub fn mesh_ears(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_ears( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1106,7 +1239,7 @@ impl QuadrupedMediumCentralSpec { "No ears specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.ears.central.0); @@ -1114,7 +1247,12 @@ impl QuadrupedMediumCentralSpec { generate_mesh(¢ral, Vec3::from(spec.ears.offset)) } - pub fn mesh_torso_f(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_torso_f( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1122,7 +1260,7 @@ impl QuadrupedMediumCentralSpec { "No torso specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.torso_f.central.0); @@ -1130,7 +1268,12 @@ impl QuadrupedMediumCentralSpec { generate_mesh(¢ral, Vec3::from(spec.torso_f.offset)) } - pub fn mesh_torso_b(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_torso_b( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1138,7 +1281,7 @@ impl QuadrupedMediumCentralSpec { "No torso specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.torso_b.central.0); @@ -1146,7 +1289,12 @@ impl QuadrupedMediumCentralSpec { generate_mesh(¢ral, Vec3::from(spec.torso_b.offset)) } - pub fn mesh_tail(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_tail( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1154,7 +1302,7 @@ impl QuadrupedMediumCentralSpec { "No tail specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let central = graceful_load_segment(&spec.tail.central.0); @@ -1169,7 +1317,12 @@ impl QuadrupedMediumLateralSpec { .unwrap() } - pub fn mesh_foot_lf(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_foot_lf( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1177,7 +1330,7 @@ impl QuadrupedMediumLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.left_front.lateral.0); @@ -1185,7 +1338,12 @@ impl QuadrupedMediumLateralSpec { generate_mesh(&lateral, Vec3::from(spec.left_front.offset)) } - pub fn mesh_foot_rf(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_foot_rf( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1193,7 +1351,7 @@ impl QuadrupedMediumLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.right_front.lateral.0); @@ -1201,7 +1359,12 @@ impl QuadrupedMediumLateralSpec { generate_mesh(&lateral, Vec3::from(spec.right_front.offset)) } - pub fn mesh_foot_lb(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_foot_lb( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1209,7 +1372,7 @@ impl QuadrupedMediumLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.left_back.lateral.0); @@ -1217,7 +1380,12 @@ impl QuadrupedMediumLateralSpec { generate_mesh(&lateral, Vec3::from(spec.left_back.offset)) } - pub fn mesh_foot_rb(&self, species: QMSpecies, body_type: QMBodyType) -> Mesh { + pub fn mesh_foot_rb( + &self, + species: QMSpecies, + body_type: QMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1225,7 +1393,7 @@ impl QuadrupedMediumLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.right_back.lateral.0); @@ -1288,7 +1456,12 @@ impl BirdMediumCenterSpec { .unwrap() } - pub fn mesh_head(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_head( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1296,7 +1469,7 @@ impl BirdMediumCenterSpec { "No head specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.head.center.0); @@ -1304,7 +1477,12 @@ impl BirdMediumCenterSpec { generate_mesh(¢er, Vec3::from(spec.head.offset)) } - pub fn mesh_torso(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_torso( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1312,7 +1490,7 @@ impl BirdMediumCenterSpec { "No torso specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.torso.center.0); @@ -1320,7 +1498,12 @@ impl BirdMediumCenterSpec { generate_mesh(¢er, Vec3::from(spec.torso.offset)) } - pub fn mesh_tail(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_tail( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1328,7 +1511,7 @@ impl BirdMediumCenterSpec { "No tail specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.tail.center.0); @@ -1342,7 +1525,12 @@ impl BirdMediumLateralSpec { .unwrap() } - pub fn mesh_wing_l(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_wing_l( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1350,7 +1538,7 @@ impl BirdMediumLateralSpec { "No wing specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.wing_l.lateral.0); @@ -1358,7 +1546,12 @@ impl BirdMediumLateralSpec { generate_mesh(&lateral, Vec3::from(spec.wing_l.offset)) } - pub fn mesh_wing_r(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_wing_r( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1366,7 +1559,7 @@ impl BirdMediumLateralSpec { "No wing specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.wing_r.lateral.0); @@ -1374,7 +1567,12 @@ impl BirdMediumLateralSpec { generate_mesh(&lateral, Vec3::from(spec.wing_r.offset)) } - pub fn mesh_foot_l(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_foot_l( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1382,7 +1580,7 @@ impl BirdMediumLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); @@ -1390,7 +1588,12 @@ impl BirdMediumLateralSpec { generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) } - pub fn mesh_foot_r(&self, species: BMSpecies, body_type: BMBodyType) -> Mesh { + pub fn mesh_foot_r( + &self, + species: BMSpecies, + body_type: BMBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1398,7 +1601,7 @@ impl BirdMediumLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); @@ -1437,7 +1640,12 @@ impl CritterCenterSpec { assets::load_watched::("voxygen.voxel.critter_center_manifest", indicator).unwrap() } - pub fn mesh_head(&self, species: CSpecies, body_type: CBodyType) -> Mesh { + pub fn mesh_head( + &self, + species: CSpecies, + body_type: CBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1445,7 +1653,7 @@ impl CritterCenterSpec { "No head specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.head.center.0); @@ -1453,7 +1661,12 @@ impl CritterCenterSpec { generate_mesh(¢er, Vec3::from(spec.head.offset)) } - pub fn mesh_chest(&self, species: CSpecies, body_type: CBodyType) -> Mesh { + pub fn mesh_chest( + &self, + species: CSpecies, + body_type: CBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1461,7 +1674,7 @@ impl CritterCenterSpec { "No chest specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.chest.center.0); @@ -1469,7 +1682,12 @@ impl CritterCenterSpec { generate_mesh(¢er, Vec3::from(spec.chest.offset)) } - pub fn mesh_feet_f(&self, species: CSpecies, body_type: CBodyType) -> Mesh { + pub fn mesh_feet_f( + &self, + species: CSpecies, + body_type: CBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1477,7 +1695,7 @@ impl CritterCenterSpec { "No feet specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.feet_f.center.0); @@ -1485,7 +1703,12 @@ impl CritterCenterSpec { generate_mesh(¢er, Vec3::from(spec.feet_f.offset)) } - pub fn mesh_feet_b(&self, species: CSpecies, body_type: CBodyType) -> Mesh { + pub fn mesh_feet_b( + &self, + species: CSpecies, + body_type: CBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1493,7 +1716,7 @@ impl CritterCenterSpec { "No feet specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.feet_b.center.0); @@ -1501,7 +1724,12 @@ impl CritterCenterSpec { generate_mesh(¢er, Vec3::from(spec.feet_b.offset)) } - pub fn mesh_tail(&self, species: CSpecies, body_type: CBodyType) -> Mesh { + pub fn mesh_tail( + &self, + species: CSpecies, + body_type: CBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1509,7 +1737,7 @@ impl CritterCenterSpec { "No tail specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.tail.center.0); @@ -1518,229 +1746,329 @@ impl CritterCenterSpec { } } //// -pub fn mesh_fish_medium_head(head: fish_medium::Head) -> Mesh { +pub fn mesh_fish_medium_head( + head: fish_medium::Head, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match head { fish_medium::Head::Default => "npc.marlin.head", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_fish_medium_torso(torso: fish_medium::Torso) -> Mesh { +pub fn mesh_fish_medium_torso( + torso: fish_medium::Torso, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match torso { fish_medium::Torso::Default => "npc.marlin.torso", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_fish_medium_rear(rear: fish_medium::Rear) -> Mesh { +pub fn mesh_fish_medium_rear( + rear: fish_medium::Rear, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match rear { fish_medium::Rear::Default => "npc.marlin.rear", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_fish_medium_tail(tail: fish_medium::Tail) -> Mesh { +pub fn mesh_fish_medium_tail( + tail: fish_medium::Tail, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match tail { fish_medium::Tail::Default => "npc.marlin.tail", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_fish_medium_fin_l(fin_l: fish_medium::FinL) -> Mesh { +pub fn mesh_fish_medium_fin_l( + fin_l: fish_medium::FinL, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match fin_l { fish_medium::FinL::Default => "npc.marlin.fin_l", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_fish_medium_fin_r(fin_r: fish_medium::FinR) -> Mesh { +pub fn mesh_fish_medium_fin_r( + fin_r: fish_medium::FinR, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match fin_r { fish_medium::FinR::Default => "npc.marlin.fin_r", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_head(head: dragon::Head) -> Mesh { +pub fn mesh_dragon_head( + head: dragon::Head, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match head { dragon::Head::Default => "npc.dragon.head", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_chest_front(chest_front: dragon::ChestFront) -> Mesh { +pub fn mesh_dragon_chest_front( + chest_front: dragon::ChestFront, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match chest_front { dragon::ChestFront::Default => "npc.dragon.chest_front", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_chest_rear(chest_rear: dragon::ChestRear) -> Mesh { +pub fn mesh_dragon_chest_rear( + chest_rear: dragon::ChestRear, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match chest_rear { dragon::ChestRear::Default => "npc.dragon.chest_rear", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_tail_front(tail_front: dragon::TailFront) -> Mesh { +pub fn mesh_dragon_tail_front( + tail_front: dragon::TailFront, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match tail_front { dragon::TailFront::Default => "npc.dragon.tail_front", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_tail_rear(tail_rear: dragon::TailRear) -> Mesh { +pub fn mesh_dragon_tail_rear( + tail_rear: dragon::TailRear, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match tail_rear { dragon::TailRear::Default => "npc.dragon.tail_rear", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_wing_in_l(wing_in_l: dragon::WingInL) -> Mesh { +pub fn mesh_dragon_wing_in_l( + wing_in_l: dragon::WingInL, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match wing_in_l { dragon::WingInL::Default => "npc.dragon.wing_in_l", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_wing_in_r(wing_in_r: dragon::WingInR) -> Mesh { +pub fn mesh_dragon_wing_in_r( + wing_in_r: dragon::WingInR, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match wing_in_r { dragon::WingInR::Default => "npc.dragon.wing_in_r", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_wing_out_l(wing_out_l: dragon::WingOutL) -> Mesh { +pub fn mesh_dragon_wing_out_l( + wing_out_l: dragon::WingOutL, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match wing_out_l { dragon::WingOutL::Default => "npc.dragon.wing_out_l", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_wing_out_r(wing_out_r: dragon::WingOutR) -> Mesh { +pub fn mesh_dragon_wing_out_r( + wing_out_r: dragon::WingOutR, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match wing_out_r { dragon::WingOutR::Default => "npc.dragon.wing_out_r", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_foot_fl(foot_fl: dragon::FootFL) -> Mesh { +pub fn mesh_dragon_foot_fl( + foot_fl: dragon::FootFL, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match foot_fl { dragon::FootFL::Default => "npc.dragon.foot_fl", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_foot_fr(foot_fr: dragon::FootFR) -> Mesh { +pub fn mesh_dragon_foot_fr( + foot_fr: dragon::FootFR, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match foot_fr { dragon::FootFR::Default => "npc.dragon.foot_fr", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_foot_bl(foot_bl: dragon::FootBL) -> Mesh { +pub fn mesh_dragon_foot_bl( + foot_bl: dragon::FootBL, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match foot_bl { dragon::FootBL::Default => "npc.dragon.foot_bl", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_dragon_foot_br(foot_br: dragon::FootBR) -> Mesh { +pub fn mesh_dragon_foot_br( + foot_br: dragon::FootBR, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match foot_br { dragon::FootBR::Default => "npc.dragon.foot_br", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } //// -pub fn mesh_bird_small_head(head: bird_small::Head) -> Mesh { +pub fn mesh_bird_small_head( + head: bird_small::Head, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match head { bird_small::Head::Default => "npc.crow.head", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_bird_small_torso(torso: bird_small::Torso) -> Mesh { +pub fn mesh_bird_small_torso( + torso: bird_small::Torso, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match torso { bird_small::Torso::Default => "npc.crow.torso", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_bird_small_wing_l(wing_l: bird_small::WingL) -> Mesh { +pub fn mesh_bird_small_wing_l( + wing_l: bird_small::WingL, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match wing_l { bird_small::WingL::Default => "npc.crow.wing_l", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_bird_small_wing_r(wing_r: bird_small::WingR) -> Mesh { +pub fn mesh_bird_small_wing_r( + wing_r: bird_small::WingR, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match wing_r { bird_small::WingR::Default => "npc.crow.wing_r", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } //// -pub fn mesh_fish_small_torso(torso: fish_small::Torso) -> Mesh { +pub fn mesh_fish_small_torso( + torso: fish_small::Torso, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match torso { fish_small::Torso::Default => "npc.cardinalfish.torso", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } -pub fn mesh_fish_small_tail(tail: fish_small::Tail) -> Mesh { +pub fn mesh_fish_small_tail( + tail: fish_small::Tail, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { load_mesh( match tail { fish_small::Tail::Default => "npc.cardinalfish.tail", }, Vec3::new(-7.0, -6.0, -6.0), + generate_mesh, ) } //// @@ -1801,7 +2129,12 @@ impl BipedLargeCenterSpec { .unwrap() } - pub fn mesh_head(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_head( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1809,7 +2142,7 @@ impl BipedLargeCenterSpec { "No head specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.head.center.0); @@ -1821,6 +2154,7 @@ impl BipedLargeCenterSpec { &self, species: BLSpecies, body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, @@ -1829,7 +2163,7 @@ impl BipedLargeCenterSpec { "No torso upper specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.torso_upper.center.0); @@ -1841,6 +2175,7 @@ impl BipedLargeCenterSpec { &self, species: BLSpecies, body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, @@ -1849,7 +2184,7 @@ impl BipedLargeCenterSpec { "No torso lower specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let center = graceful_load_segment(&spec.torso_lower.center.0); @@ -1867,6 +2202,7 @@ impl BipedLargeLateralSpec { &self, species: BLSpecies, body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, @@ -1875,7 +2211,7 @@ impl BipedLargeLateralSpec { "No shoulder specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.shoulder_l.lateral.0); @@ -1887,6 +2223,7 @@ impl BipedLargeLateralSpec { &self, species: BLSpecies, body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, @@ -1895,7 +2232,7 @@ impl BipedLargeLateralSpec { "No shoulder specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.shoulder_r.lateral.0); @@ -1903,7 +2240,12 @@ impl BipedLargeLateralSpec { generate_mesh(&lateral, Vec3::from(spec.shoulder_r.offset)) } - pub fn mesh_hand_l(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_hand_l( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1911,7 +2253,7 @@ impl BipedLargeLateralSpec { "No hand specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.hand_l.lateral.0); @@ -1919,7 +2261,12 @@ impl BipedLargeLateralSpec { generate_mesh(&lateral, Vec3::from(spec.hand_l.offset)) } - pub fn mesh_hand_r(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_hand_r( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1927,7 +2274,7 @@ impl BipedLargeLateralSpec { "No hand specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.hand_r.lateral.0); @@ -1935,7 +2282,12 @@ impl BipedLargeLateralSpec { generate_mesh(&lateral, Vec3::from(spec.hand_r.offset)) } - pub fn mesh_leg_l(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_leg_l( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1943,7 +2295,7 @@ impl BipedLargeLateralSpec { "No leg specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.leg_l.lateral.0); @@ -1951,7 +2303,12 @@ impl BipedLargeLateralSpec { generate_mesh(&lateral, Vec3::from(spec.leg_l.offset)) } - pub fn mesh_leg_r(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_leg_r( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1959,7 +2316,7 @@ impl BipedLargeLateralSpec { "No leg specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.leg_r.lateral.0); @@ -1967,7 +2324,12 @@ impl BipedLargeLateralSpec { generate_mesh(&lateral, Vec3::from(spec.leg_r.offset)) } - pub fn mesh_foot_l(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_foot_l( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1975,7 +2337,7 @@ impl BipedLargeLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.foot_l.lateral.0); @@ -1983,7 +2345,12 @@ impl BipedLargeLateralSpec { generate_mesh(&lateral, Vec3::from(spec.foot_l.offset)) } - pub fn mesh_foot_r(&self, species: BLSpecies, body_type: BLBodyType) -> Mesh { + pub fn mesh_foot_r( + &self, + species: BLSpecies, + body_type: BLBodyType, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, + ) -> Mesh { let spec = match self.0.get(&(species, body_type)) { Some(spec) => spec, None => { @@ -1991,7 +2358,7 @@ impl BipedLargeLateralSpec { "No foot specification exists for the combination of {:?} and {:?}", species, body_type ); - return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5)); + return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5), generate_mesh); }, }; let lateral = graceful_load_segment(&spec.foot_r.lateral.0); @@ -2000,7 +2367,10 @@ impl BipedLargeLateralSpec { } } //// -pub fn mesh_object(obj: object::Body) -> Mesh { +pub fn mesh_object( + obj: object::Body, + generate_mesh: impl FnOnce(&Segment, Vec3) -> Mesh, +) -> Mesh { use object::Body; let (name, offset) = match obj { @@ -2064,5 +2434,5 @@ pub fn mesh_object(obj: object::Body) -> Mesh { Body::BoltFire => ("weapon.projectile.fire-bolt-0", Vec3::new(-3.0, -5.5, -3.0)), Body::BoltFireBig => ("weapon.projectile.fire-bolt-1", Vec3::new(-6.0, -6.0, -6.0)), }; - load_mesh(name, offset) + load_mesh(name, offset, generate_mesh) } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 545feec3c3..933451dfb6 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -118,17 +118,20 @@ impl FigureMgr { .map_or(Vec3::zero(), |pos| pos.0); for ( - entity, - pos, - interpolated, - vel, - scale, - body, - character, - last_character, - physics, - stats, - loadout, + i, + ( + entity, + pos, + interpolated, + vel, + scale, + body, + character, + last_character, + physics, + stats, + loadout, + ), ) in ( &ecs.entities(), &ecs.read_storage::(), @@ -143,7 +146,26 @@ impl FigureMgr { ecs.read_storage::().maybe(), ) .join() + .enumerate() { + // 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 + // much about the update *rate* for far away things. As the entity + // goes further and further away, we start to 'skip' update ticks. + // 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) + - MIN_PERFECT_RATE_DIST.powf(0.5)) + .max(0.0) + / 3.0) as u64) + != 0 + { + continue; + } + let is_player = scene_data.player_entity == entity; let (pos, ori) = interpolated @@ -1386,7 +1408,7 @@ impl FigureMgr { let character_state_storage = state.read_storage::(); let character_state = character_state_storage.get(player_entity); - for (entity, _, _, body, _, loadout, _) in ( + for (entity, pos, _, body, _, loadout, _) in ( &ecs.entities(), &ecs.read_storage::(), ecs.read_storage::().maybe(), @@ -1415,6 +1437,7 @@ impl FigureMgr { body, loadout, false, + pos.0, ); } } @@ -1437,7 +1460,10 @@ impl FigureMgr { let character_state_storage = state.read_storage::(); let character_state = character_state_storage.get(player_entity); - if let Some(body) = ecs.read_storage::().get(player_entity) { + if let (Some(pos), Some(body)) = ( + ecs.read_storage::().get(player_entity), + ecs.read_storage::().get(player_entity), + ) { let stats_storage = state.read_storage::(); let stats = stats_storage.get(player_entity); @@ -1461,6 +1487,7 @@ impl FigureMgr { body, loadout, true, + pos.0, ); } } @@ -1479,6 +1506,7 @@ impl FigureMgr { body: &Body, loadout: Option<&Loadout>, is_player: bool, + pos: Vec3, ) { let player_camera_mode = if is_player { camera.get_mode() @@ -1691,6 +1719,19 @@ impl FigureMgr { ) }), } { + const FIGURE_LOW_LOD_DIST: f32 = 150.0; + const FIGURE_MID_LOD_DIST: f32 = 85.0; + + let model = if pos.distance_squared(camera.get_focus_pos()) + > FIGURE_LOW_LOD_DIST.powf(2.0) + { + &model[2] + } else if pos.distance_squared(camera.get_focus_pos()) > FIGURE_MID_LOD_DIST.powf(2.0) { + &model[1] + } else { + &model[0] + }; + if is_player { renderer.render_player( model, @@ -1850,7 +1891,10 @@ impl FigureState { renderer.update_consts(&mut self.locals, &[locals]).unwrap(); renderer - .update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()) + .update_consts( + &mut self.bone_consts, + &self.skeleton.compute_matrices()[0..self.skeleton.bone_count()], + ) .unwrap(); } diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 8725b089c8..da18c09b58 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -85,6 +85,7 @@ pub struct SceneData<'a> { pub thread_pool: &'a uvth::ThreadPool, pub gamma: f32, pub mouse_smoothing: bool, + pub sprite_render_distance: f32, } impl Scene { @@ -365,6 +366,7 @@ impl Scene { self.select_pos, scene_data.gamma, self.camera.get_mode(), + scene_data.sprite_render_distance as f32 - 20.0, )]) .expect("Failed to update global constants"); @@ -401,6 +403,7 @@ impl Scene { state: &State, player_entity: EcsEntity, tick: u64, + scene_data: &SceneData, ) { // Render terrain and figures. self.terrain.render( @@ -446,6 +449,7 @@ impl Scene { &self.shadows, &self.lod, self.camera.get_focus_pos(), + scene_data.sprite_render_distance, ); renderer.render_post_process( diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 8e936b9ae3..d7378400c7 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -4,8 +4,9 @@ use crate::{ fixture::FixtureSkeleton, Animation, Skeleton, }, + mesh::Meshable, render::{ - create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Model, + create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Mesh, Model, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, }, scene::{ @@ -19,6 +20,7 @@ use crate::{ use client::Client; use common::{ comp::{humanoid, Body, Loadout}, + figure::Segment, terrain::BlockKind, vol::{BaseVol, ReadVol, Vox}, }; @@ -43,6 +45,10 @@ 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 +} + struct Skybox { model: Model, locals: Consts, @@ -120,7 +126,11 @@ impl Scene { backdrop: backdrop.map(|specifier| { ( renderer - .create_model(&load_mesh(specifier, Vec3::new(-55.0, -49.5, -2.0))) + .create_model(&load_mesh( + specifier, + Vec3::new(-55.0, -49.5, -2.0), + generate_mesh, + )) .unwrap(), FigureState::new(renderer, FixtureSkeleton::new()), ) @@ -188,6 +198,7 @@ impl Scene { None, scene_data.gamma, self.camera.get_mode(), + 250.0, )]) { error!("Renderer failed to update: {:?}", err); } @@ -244,7 +255,7 @@ impl Scene { .0; renderer.render_figure( - model, + &model[0], &self.globals, self.figure_state.locals(), self.figure_state.bone_consts(), diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index d6249ef9a4..8163e9e853 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -314,7 +314,7 @@ pub struct Terrain { mesh_todo: HashMap, ChunkMeshState>, // GPU data - sprite_models: HashMap<(BlockKind, usize), Model>, + sprite_models: HashMap<(BlockKind, usize), Vec>>, waves: Texture, phantom: PhantomData, @@ -326,16 +326,30 @@ impl Terrain { // with worker threads that are meshing chunks. let (send, recv) = channel::unbounded(); - let mut make_model = |s, offset| { - renderer - .create_model( - &Meshable::::generate_mesh( - &Segment::from(assets::load_expect::(s).as_ref()), - offset, - ) - .0, - ) - .unwrap() + let mut make_models = |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::>() }; Self { @@ -347,1267 +361,1506 @@ impl Terrain { // Windows ( (BlockKind::Window1, 0), - make_model( + make_models( "voxygen.voxel.sprite.window.window-0", Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), ), ), ( (BlockKind::Window2, 0), - make_model( + make_models( "voxygen.voxel.sprite.window.window-1", Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), ), ), ( (BlockKind::Window3, 0), - make_model( + make_models( "voxygen.voxel.sprite.window.window-2", Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), ), ), ( (BlockKind::Window4, 0), - make_model( + make_models( "voxygen.voxel.sprite.window.window-3", Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), ), ), // Cacti ( (BlockKind::LargeCactus, 0), - make_model( + make_models( "voxygen.voxel.sprite.cacti.large_cactus", Vec3::new(-13.5, -5.5, 0.0), + Vec3::one(), ), ), ( (BlockKind::LargeCactus, 1), - make_model( + make_models( "voxygen.voxel.sprite.cacti.tall", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BarrelCactus, 0), - make_model( + make_models( "voxygen.voxel.sprite.cacti.barrel_cactus", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::RoundCactus, 0), - make_model( + make_models( "voxygen.voxel.sprite.cacti.cactus_round", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortCactus, 0), - make_model( + make_models( "voxygen.voxel.sprite.cacti.cactus_short", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::MedFlatCactus, 0), - make_model( + make_models( "voxygen.voxel.sprite.cacti.flat_cactus_med", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortFlatCactus, 0), - make_model( + make_models( "voxygen.voxel.sprite.cacti.flat_cactus_short", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), // Fruit ( (BlockKind::Apple, 0), - make_model( + make_models( "voxygen.voxel.sprite.fruit.apple", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), // Flowers ( (BlockKind::BlueFlower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BlueFlower, 1), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BlueFlower, 2), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BlueFlower, 3), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_4", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BlueFlower, 4), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_5", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BlueFlower, 5), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_6", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::BlueFlower, 6), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_blue_7", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::PinkFlower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_pink_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::PinkFlower, 1), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_pink_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::PinkFlower, 2), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_pink_3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::PinkFlower, 3), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_pink_4", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::PurpleFlower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_purple_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::RedFlower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_red_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::RedFlower, 1), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_red_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::RedFlower, 2), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_red_3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::WhiteFlower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_white_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::WhiteFlower, 1), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_white_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::YellowFlower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.flower_purple_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Sunflower, 0), - make_model( + make_models( "voxygen.voxel.sprite.flowers.sunflower_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Sunflower, 1), - make_model( + make_models( "voxygen.voxel.sprite.flowers.sunflower_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), // Grass ( (BlockKind::LongGrass, 0), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::LongGrass, 1), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::LongGrass, 2), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::LongGrass, 3), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_4", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::LongGrass, 4), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_5", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::LongGrass, 5), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_6", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::LongGrass, 6), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_long_7", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::MediumGrass, 0), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_med_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::MediumGrass, 1), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_med_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::MediumGrass, 2), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_med_3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::MediumGrass, 3), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_med_4", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::MediumGrass, 4), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_med_5", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortGrass, 0), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_short_1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortGrass, 1), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_short_2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortGrass, 2), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_short_3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortGrass, 3), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_short_4", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::ShortGrass, 4), - make_model( + make_models( "voxygen.voxel.sprite.grass.grass_short_5", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 0), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-0", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 1), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 2), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 3), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-3", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 4), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-4", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 5), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-5", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 6), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-6", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 7), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-7", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 8), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-8", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 9), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-9", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Mushroom, 10), - make_model( + make_models( "voxygen.voxel.sprite.mushrooms.mushroom-10", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Liana, 0), - make_model( + 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_model( + 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_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_ore", Vec3::new(-5.0, -5.0, -5.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 0), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_1", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 1), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_2", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 2), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_3", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 3), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_4", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 4), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_5", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 5), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_6", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 6), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_7", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 7), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_8", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 8), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_9", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::VeloriteFrag, 9), - make_model( + make_models( "voxygen.voxel.sprite.velorite.velorite_10", Vec3::new(-3.0, -5.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Chest, 0), - make_model( + make_models( "voxygen.voxel.sprite.chests.chest", Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Chest, 1), - make_model( + make_models( "voxygen.voxel.sprite.chests.chest_gold", Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Chest, 2), - make_model( + make_models( "voxygen.voxel.sprite.chests.chest_dark", Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Chest, 3), - make_model( + make_models( "voxygen.voxel.sprite.chests.chest_vines", Vec3::new(-7.0, -5.0, -0.0), + Vec3::one(), ), ), //Welwitch ( (BlockKind::Welwitch, 0), - make_model( + 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_model( + make_models( "voxygen.voxel.sprite.pumpkin.1", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Pumpkin, 1), - make_model( + make_models( "voxygen.voxel.sprite.pumpkin.2", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Pumpkin, 2), - make_model( + make_models( "voxygen.voxel.sprite.pumpkin.3", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Pumpkin, 3), - make_model( + make_models( "voxygen.voxel.sprite.pumpkin.4", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Pumpkin, 4), - make_model( + make_models( "voxygen.voxel.sprite.pumpkin.5", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Pumpkin, 5), - make_model( + make_models( "voxygen.voxel.sprite.pumpkin.6", Vec3::new(-7.0, -6.5, -0.0), + Vec3::one(), ), ), ( (BlockKind::Pumpkin, 6), - make_model( + make_models( "voxygen.voxel.sprite.pumpkin.7", Vec3::new(-7.0, -9.5, -0.0), + Vec3::one(), ), ), //Lingonberries ( (BlockKind::LingonBerry, 0), - make_model( + make_models( "voxygen.voxel.sprite.lingonberry.1", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LingonBerry, 1), - make_model( + make_models( "voxygen.voxel.sprite.lingonberry.2", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LingonBerry, 2), - make_model( + make_models( "voxygen.voxel.sprite.lingonberry.3", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), // Leafy Plants ( (BlockKind::LeafyPlant, 0), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.1", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 1), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.2", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 2), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.3", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 3), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.4", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 4), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.5", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 5), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.6", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 6), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.7", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 7), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.8", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 8), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.9", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::LeafyPlant, 9), - make_model( + make_models( "voxygen.voxel.sprite.leafy_plant.10", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), // Ferns ( (BlockKind::Fern, 0), - make_model("voxygen.voxel.sprite.ferns.1", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.1", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 1), - make_model("voxygen.voxel.sprite.ferns.2", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.2", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 2), - make_model("voxygen.voxel.sprite.ferns.3", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.3", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 3), - make_model("voxygen.voxel.sprite.ferns.4", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.4", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 4), - make_model("voxygen.voxel.sprite.ferns.5", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.5", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 5), - make_model("voxygen.voxel.sprite.ferns.6", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.6", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 6), - make_model("voxygen.voxel.sprite.ferns.7", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.7", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 7), - make_model("voxygen.voxel.sprite.ferns.8", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.8", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 8), - make_model("voxygen.voxel.sprite.ferns.9", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.9", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 9), - make_model("voxygen.voxel.sprite.ferns.10", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.10", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 10), - make_model("voxygen.voxel.sprite.ferns.11", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.11", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), ( (BlockKind::Fern, 11), - make_model("voxygen.voxel.sprite.ferns.12", Vec3::new(-6.0, -6.0, -0.0)), + make_models( + "voxygen.voxel.sprite.ferns.12", + Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), + ), ), // Dead Bush ( (BlockKind::DeadBush, 0), - make_model( + make_models( "voxygen.voxel.sprite.dead_bush.1", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::DeadBush, 1), - make_model( + make_models( "voxygen.voxel.sprite.dead_bush.2", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::DeadBush, 2), - make_model( + make_models( "voxygen.voxel.sprite.dead_bush.3", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::DeadBush, 3), - make_model( + make_models( "voxygen.voxel.sprite.dead_bush.4", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), // Blueberries ( (BlockKind::Blueberry, 0), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.1", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 1), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.2", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 2), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.3", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 3), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.4", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 4), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.5", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 5), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.6", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 6), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.7", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 7), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.8", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), ( (BlockKind::Blueberry, 8), - make_model( + make_models( "voxygen.voxel.sprite.blueberry.9", Vec3::new(-6.0, -6.0, -0.0), + Vec3::one(), ), ), // Ember ( (BlockKind::Ember, 0), - make_model("voxygen.voxel.sprite.ember.1", Vec3::new(-7.0, -7.0, -2.9)), + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + make_models( "voxygen.voxel.sprite.cabbage.cabbage-0", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Cabbage, 1), - make_model( + make_models( "voxygen.voxel.sprite.cabbage.cabbage-1", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), ( (BlockKind::Cabbage, 2), - make_model( + make_models( "voxygen.voxel.sprite.cabbage.cabbage-2", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), // Flax ( (BlockKind::Flax, 0), - make_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + 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_model( + make_models( "voxygen.voxel.sprite.carrot.0", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Carrot, 1), - make_model( + make_models( "voxygen.voxel.sprite.carrot.1", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Carrot, 2), - make_model( + make_models( "voxygen.voxel.sprite.carrot.2", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Carrot, 3), - make_model( + make_models( "voxygen.voxel.sprite.carrot.3", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Carrot, 4), - make_model( + make_models( "voxygen.voxel.sprite.carrot.4", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Carrot, 5), - make_model( + make_models( "voxygen.voxel.sprite.carrot.5", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Tomato, 0), - make_model("voxygen.voxel.sprite.tomato.0", Vec3::new(-5.5, -5.5, 0.0)), + make_models( + "voxygen.voxel.sprite.tomato.0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), ), ( (BlockKind::Tomato, 1), - make_model("voxygen.voxel.sprite.tomato.1", Vec3::new(-5.5, -5.5, 0.0)), + make_models( + "voxygen.voxel.sprite.tomato.1", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), ), ( (BlockKind::Tomato, 2), - make_model("voxygen.voxel.sprite.tomato.2", Vec3::new(-5.5, -5.5, 0.0)), + make_models( + "voxygen.voxel.sprite.tomato.2", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), ), ( (BlockKind::Tomato, 3), - make_model("voxygen.voxel.sprite.tomato.3", Vec3::new(-5.5, -5.5, 0.0)), + make_models( + "voxygen.voxel.sprite.tomato.3", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), ), ( (BlockKind::Tomato, 4), - make_model("voxygen.voxel.sprite.tomato.4", Vec3::new(-5.5, -5.5, 0.0)), + make_models( + "voxygen.voxel.sprite.tomato.4", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), ), // Radish ( (BlockKind::Radish, 0), - make_model( + make_models( "voxygen.voxel.sprite.radish.0", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Radish, 1), - make_model( + make_models( "voxygen.voxel.sprite.radish.1", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Radish, 2), - make_model( + make_models( "voxygen.voxel.sprite.radish.2", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Radish, 3), - make_model( + make_models( "voxygen.voxel.sprite.radish.3", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Radish, 4), - make_model( + make_models( "voxygen.voxel.sprite.radish.4", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), // Turnip ( (BlockKind::Turnip, 0), - make_model( + make_models( "voxygen.voxel.sprite.turnip.turnip-0", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Turnip, 1), - make_model( + make_models( "voxygen.voxel.sprite.turnip.turnip-1", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Turnip, 2), - make_model( + make_models( "voxygen.voxel.sprite.turnip.turnip-2", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Turnip, 3), - make_model( + make_models( "voxygen.voxel.sprite.turnip.turnip-3", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Turnip, 4), - make_model( + make_models( "voxygen.voxel.sprite.turnip.turnip-4", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), ( (BlockKind::Turnip, 5), - make_model( + make_models( "voxygen.voxel.sprite.turnip.turnip-5", Vec3::new(-5.5, -5.5, -0.25), + Vec3::one(), ), ), // Coconut ( (BlockKind::Coconut, 0), - make_model( + make_models( "voxygen.voxel.sprite.fruit.coconut", Vec3::new(-6.0, -6.0, 0.0), + Vec3::one(), ), ), // Scarecrow ( (BlockKind::Scarecrow, 0), - make_model( + make_models( "voxygen.voxel.sprite.misc.scarecrow", Vec3::new(-9.5, -3.0, -0.25), + Vec3::unit_z(), ), ), // Street Light ( (BlockKind::StreetLamp, 0), - make_model( + make_models( "voxygen.voxel.sprite.misc.street_lamp", Vec3::new(-4.5, -4.5, 0.0), + Vec3::unit_z(), ), ), // Door ( (BlockKind::Door, 0), - make_model( + make_models( "voxygen.voxel.sprite.door.door-0", Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), ), ), ] @@ -1976,6 +2229,7 @@ impl Terrain { shadows: &Consts, lod: &Lod, focus_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) @@ -1991,16 +2245,28 @@ impl Terrain { // Terrain sprites for (pos, chunk) in chunk_iter.clone() { if chunk.visible { - const SPRITE_RENDER_DISTANCE: f32 = 128.0; + 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); - if Vec2::from(focus_pos).distance_squared(chunk_center) - < SPRITE_RENDER_DISTANCE * SPRITE_RENDER_DISTANCE - { + 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 { renderer.render_sprites( - &self.sprite_models[&kind], + if dist_sqrd < sprite_high_detail_distance.powf(2.0) { + &self.sprite_models[&kind][0] + } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][1] + } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][2] + } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { + &self.sprite_models[&kind][3] + } else { + &self.sprite_models[&kind][4] + }, globals, &instances, lights, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 4dc84d10f3..973364622c 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -4,7 +4,6 @@ use crate::{ i18n::{i18n_asset_key, VoxygenLocalization}, key_state::KeyState, menu::char_selection::CharSelectionState, - render::Renderer, scene::{camera, Scene, SceneData}, settings::AudioOutput, window::{AnalogGameInput, Event, GameInput}, @@ -115,26 +114,6 @@ impl SessionState { /// Clean up the session (and the client attached to it) after a tick. pub fn cleanup(&mut self) { self.client.borrow_mut().cleanup(); } - - /// Render the session to the screen. - /// - /// This method should be called once per frame. - pub fn render(&mut self, renderer: &mut Renderer) { - // Clear the screen - renderer.clear(); - - // Render the screen using the global renderer - { - let client = self.client.borrow(); - self.scene - .render(renderer, client.state(), client.entity(), client.get_tick()); - } - // Draw the UI to the screen - self.hud.render(renderer, self.scene.globals()); - - // Finish the frame - renderer.flush(); - } } impl PlayState for SessionState { @@ -599,6 +578,11 @@ impl PlayState for SessionState { 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; + global_state.settings.save_to_file_warn(); + }, HudEvent::CrosshairTransp(crosshair_transp) => { global_state.settings.gameplay.crosshair_transp = crosshair_transp; global_state.settings.save_to_file_warn(); @@ -730,9 +714,6 @@ impl PlayState for SessionState { } } - // Runs if either in a multiplayer server or the singleplayer server is unpaused - if global_state.singleplayer.is_none() - || !global_state.singleplayer.as_ref().unwrap().is_paused() { let client = self.client.borrow(); let scene_data = SceneData { @@ -744,17 +725,38 @@ impl PlayState for SessionState { thread_pool: client.thread_pool(), gamma: global_state.settings.graphics.gamma, mouse_smoothing: global_state.settings.gameplay.smooth_pan_enable, + sprite_render_distance: global_state.settings.graphics.sprite_render_distance + as f32, }; - self.scene.maintain( - global_state.window.renderer_mut(), - &mut global_state.audio, + + // Runs if either in a multiplayer server or the singleplayer server is unpaused + if global_state.singleplayer.is_none() + || !global_state.singleplayer.as_ref().unwrap().is_paused() + { + self.scene.maintain( + global_state.window.renderer_mut(), + &mut global_state.audio, + &scene_data, + ); + } + + let renderer = global_state.window.renderer_mut(); + // Clear the screen + renderer.clear(); + // Render the screen using the global renderer + self.scene.render( + renderer, + client.state(), + client.entity(), + client.get_tick(), &scene_data, ); + // Draw the UI to the screen + self.hud.render(renderer, self.scene.globals()); + // Finish the frame + renderer.flush(); } - // Render the session. - self.render(global_state.window.renderer_mut()); - // Display the frame on the window. global_state .window diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index baec31a12b..cca088f2e9 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -548,6 +548,7 @@ impl Default for Log { #[serde(default)] pub struct GraphicsSettings { pub view_distance: u32, + pub sprite_render_distance: u32, pub max_fps: u32, pub fov: u16, pub gamma: f32, @@ -563,6 +564,7 @@ impl Default for GraphicsSettings { fn default() -> Self { Self { view_distance: 10, + sprite_render_distance: 250, max_fps: 60, fov: 50, gamma: 1.0, diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index a8a9d83846..1d1515159a 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -140,12 +140,13 @@ impl Civs { e * sz as i32 + sz as i32 / 2 }); + let mut rng = ctx.reseed().rng; let world_site = match &site.kind { SiteKind::Settlement => { - WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), &mut ctx.rng)) + WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) }, SiteKind::Dungeon => { - WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), &mut ctx.rng)) + WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), &mut rng)) }, }; diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index cc499d61e2..ebe671f544 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -116,7 +116,7 @@ impl Archetype for House { storey_fill: StoreyFill::All, mansard: 0, pillar: match rng.gen_range(0, 3) { - 0 => Pillar::Chimney(9 + locus + rng.gen_range(0, 4)), + 0 => Pillar::Chimney(10 + locus + rng.gen_range(0, 4)), 1 => Pillar::Tower(15 + locus + rng.gen_range(0, 4)), _ => Pillar::None, }, @@ -199,7 +199,8 @@ impl Archetype for House { let facade_layer = 3; let structural_layer = facade_layer + 1; - let foundation_layer = structural_layer + 1; + let internal_layer = structural_layer + 1; + let foundation_layer = internal_layer + 1; let floor_layer = foundation_layer + 1; let foundation = make_block(100, 100, 100).with_priority(foundation_layer); @@ -207,9 +208,9 @@ impl Archetype for House { let floor = make_block(100, 75, 50); let wall = make_block(200, 180, 150).with_priority(facade_layer); let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b) - .with_priority(facade_layer); + .with_priority(facade_layer - 1); let empty = BlockMask::nothing(); - let internal = BlockMask::new(Block::empty(), structural_layer); + let internal = BlockMask::new(Block::empty(), internal_layer); let end_window = BlockMask::new( Block::new(BlockKind::Window1, make_meta(ori.flip())), structural_layer,