lod for sprites on volumes

This commit is contained in:
Isse 2023-02-22 10:11:57 +01:00
parent fcb7011cde
commit d292234c47
18 changed files with 254 additions and 162 deletions

View File

@ -173,13 +173,13 @@ pub mod figuredata {
impl DeBlock {
fn to_block(&self, color: Rgb<u8>) -> Block {
match self {
&DeBlock::Block(block) => Block::new(block, color),
&DeBlock::Air(sprite, ori) => {
match *self {
DeBlock::Block(block) => Block::new(block, color),
DeBlock::Air(sprite, ori) => {
let block = Block::new(BlockKind::Air, color).with_sprite(sprite);
block.with_ori(ori).unwrap_or(block)
},
&DeBlock::Water(sprite, ori) => {
DeBlock::Water(sprite, ori) => {
let block = Block::new(BlockKind::Water, color).with_sprite(sprite);
block.with_ori(ori).unwrap_or(block)
},

View File

@ -9,8 +9,9 @@ pub use self::{
};
use crate::{
terrain::{Block, BlockKind},
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol},
volumes::dyna::Dyna, terrain::{Block, BlockKind},
volumes::dyna::Dyna,
};
use dot_vox::DotVoxData;
use vek::*;
@ -19,19 +20,17 @@ pub type TerrainSegment = Dyna<Block, ()>;
impl From<Segment> for TerrainSegment {
fn from(value: Segment) -> Self {
TerrainSegment::from_fn(value.sz, (), |pos| {
match value.get(pos) {
Err(_) | Ok(Cell::Empty) => Block::empty(),
Ok(cell) => {
if cell.is_hollow() {
Block::empty()
} else if cell.is_glowy() {
Block::new(BlockKind::GlowingRock, cell.get_color().unwrap())
} else {
Block::new(BlockKind::Misc, cell.get_color().unwrap())
}
TerrainSegment::from_fn(value.sz, (), |pos| match value.get(pos) {
Err(_) | Ok(Cell::Empty) => Block::empty(),
Ok(cell) => {
if cell.is_hollow() {
Block::empty()
} else if cell.is_glowy() {
Block::new(BlockKind::GlowingRock, cell.get_color().unwrap())
} else {
Block::new(BlockKind::Misc, cell.get_color().unwrap())
}
}
},
})
}
}

View File

@ -59,7 +59,12 @@ pub trait CharacterBehavior {
fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
StateUpdate::from(data)
}
fn mount_sprite(&self, data: &JoinData, _output_events: &mut OutputEvents, _pos: Vec3<i32>) -> StateUpdate {
fn mount_sprite(
&self,
data: &JoinData,
_output_events: &mut OutputEvents,
_pos: Vec3<i32>,
) -> StateUpdate {
StateUpdate::from(data)
}
// start_input has custom implementation in the following character states that

View File

@ -22,6 +22,7 @@ pub mod glide_wield;
pub mod idle;
pub mod leap_melee;
pub mod leap_shockwave;
pub mod mount_sprite;
pub mod music;
pub mod rapid_melee;
pub mod repeater_ranged;
@ -40,4 +41,3 @@ pub mod use_item;
pub mod utils;
pub mod wallrun;
pub mod wielding;
pub mod mount_sprite;

View File

@ -1,9 +1,16 @@
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use vek::Vec3;
use crate::{comp::{character_state::OutputEvents, StateUpdate, CharacterState}, util::Dir};
use crate::{
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
util::Dir,
};
use super::{behavior::{CharacterBehavior, JoinData}, utils::{handle_orientation, end_ability, handle_wield}, idle};
use super::{
behavior::{CharacterBehavior, JoinData},
idle,
utils::{end_ability, handle_orientation, handle_wield},
};
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
@ -13,7 +20,6 @@ pub struct StaticData {
pub sprite_pos: Vec3<i32>,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// Struct containing data that does not change over the course of the
@ -27,7 +33,12 @@ impl CharacterBehavior for Data {
update.pos.0 = self.static_data.mount_pos;
update.vel.0 = Vec3::zero();
handle_orientation(data, &mut update, 1.0, Some(Dir::new(self.static_data.mount_dir)));
handle_orientation(
data,
&mut update,
1.0,
Some(Dir::new(self.static_data.mount_dir)),
);
handle_wield(data, &mut update);
@ -46,4 +57,4 @@ impl CharacterBehavior for Data {
update
}
}
}

View File

@ -21,7 +21,7 @@ use crate::{
event::{LocalEvent, ServerEvent},
outcome::Outcome,
states::{behavior::JoinData, utils::CharacterState::Idle, *},
terrain::{TerrainGrid, UnlockKind, SpriteKind},
terrain::{SpriteKind, TerrainGrid, UnlockKind},
util::Dir,
vol::ReadVol,
};
@ -785,26 +785,40 @@ pub fn attempt_sit(data: &JoinData<'_>, update: &mut StateUpdate) {
}
}
pub fn sprite_mount_points(sprite_kind: SpriteKind, pos: Vec3<i32>, ori: u8) -> impl ExactSizeIterator<Item = (Vec3<f32>, Vec3<f32>)> {
pub fn sprite_mount_points(
sprite_kind: SpriteKind,
pos: Vec3<i32>,
ori: u8,
) -> impl ExactSizeIterator<Item = (Vec3<f32>, Vec3<f32>)> {
let mat = Mat4::identity()
.rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
.translated_3d(
pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)
);
sprite_kind.mount_offsets().iter().map(move |(pos, dir)| {
((mat * pos.with_w(1.0)).xyz(), (mat * dir.with_w(0.0)).xyz())
})
}
.rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
.translated_3d(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0));
sprite_kind
.mount_offsets()
.iter()
.map(move |(pos, dir)| ((mat * pos.with_w(1.0)).xyz(), (mat * dir.with_w(0.0)).xyz()))
}
const SPRITE_MOUNT_RANGE: f32 = 2.0;
pub const SPRITE_MOUNT_RANGE_SQR: f32 = SPRITE_MOUNT_RANGE * SPRITE_MOUNT_RANGE;
pub fn attempt_mount_sprite(data: &JoinData<'_>, update: &mut StateUpdate, pos: Vec3<i32>) {
if let Some((kind, ori)) = data.terrain.get(pos).ok().and_then(|block| block.get_sprite().zip(block.get_ori())) {
if let Some((mount_pos, mount_dir)) = sprite_mount_points(kind, pos, ori).min_by_key(|(pos, _)| {
OrderedFloat(data.pos.0.distance_squared(*pos))
}) {
if reach_block(data.pos.0, pos, SPRITE_MOUNT_RANGE_SQR, data.body, data.terrain) {
if let Some((kind, ori)) = data
.terrain
.get(pos)
.ok()
.and_then(|block| block.get_sprite().zip(block.get_ori()))
{
if let Some((mount_pos, mount_dir)) = sprite_mount_points(kind, pos, ori)
.min_by_key(|(pos, _)| OrderedFloat(data.pos.0.distance_squared(*pos)))
{
if reach_block(
data.pos.0,
pos,
SPRITE_MOUNT_RANGE_SQR,
data.body,
data.terrain,
) {
update.character = CharacterState::MountSprite(mount_sprite::Data {
static_data: mount_sprite::StaticData {
mount_pos,
@ -887,13 +901,18 @@ pub fn attempt_swap_equipped_weapons(data: &JoinData<'_>, update: &mut StateUpda
}
}
fn reach_block(player_pos: Vec3<f32>, block_pos: Vec3<i32>, range: f32, body: &Body, terrain: &TerrainGrid) -> bool {
fn reach_block(
player_pos: Vec3<f32>,
block_pos: Vec3<i32>,
range: f32,
body: &Body,
terrain: &TerrainGrid,
) -> bool {
let block_pos_f32 = block_pos.map(|x| x as f32 + 0.5);
// Closure to check if distance between a point and the block is less than
// MAX_PICKUP_RANGE and the radius of the body
let block_range_check = |pos: Vec3<f32>| {
(block_pos_f32 - pos).magnitude_squared()
< (range + body.max_radius()).powi(2)
(block_pos_f32 - pos).magnitude_squared() < (range + body.max_radius()).powi(2)
};
// Checks if player's feet or head is near to block
@ -903,12 +922,10 @@ fn reach_block(player_pos: Vec3<f32>, block_pos: Vec3<i32>, range: f32, body: &B
// Do a check that a path can be found between sprite and entity
// interacting with sprite Use manhattan distance * 1.5 for number
// of iterations
let iters =
(3.0 * (block_pos_f32 - player_pos).map(|x| x.abs()).sum()) as usize;
let iters = (3.0 * (block_pos_f32 - player_pos).map(|x| x.abs()).sum()) as usize;
// Heuristic compares manhattan distance of start and end pos
let heuristic = move |pos: &Vec3<i32>, _: &Vec3<i32>| {
(block_pos - pos).map(|x| x.abs()).sum() as f32
};
let heuristic =
move |pos: &Vec3<i32>, _: &Vec3<i32>| (block_pos - pos).map(|x| x.abs()).sum() as f32;
let mut astar = Astar::new(
iters,
@ -994,18 +1011,27 @@ pub fn handle_manipulate_loadout(
},
InventoryAction::Collect(sprite_pos) => {
// First, get sprite data for position, if there is a sprite
let sprite_at_pos = data.terrain
let sprite_at_pos = data
.terrain
.get(sprite_pos)
.ok()
.copied()
.and_then(|b| b.get_sprite());
// Checks if position has a collectible sprite as well as what sprite is at the
// position
let sprite_interact = sprite_at_pos.and_then(Option::<sprite_interact::SpriteInteractKind>::from);
let sprite_interact =
sprite_at_pos.and_then(Option::<sprite_interact::SpriteInteractKind>::from);
if let Some(sprite_interact) = sprite_interact {
if reach_block(data.pos.0, sprite_pos, MAX_PICKUP_RANGE, data.body, data.terrain) {
if reach_block(
data.pos.0,
sprite_pos,
MAX_PICKUP_RANGE,
data.body,
data.terrain,
) {
let sprite_chunk_pos = TerrainGrid::chunk_offs(sprite_pos);
let sprite_cfg = data.terrain
let sprite_cfg = data
.terrain
.pos_chunk(sprite_pos)
.and_then(|chunk| chunk.meta().sprite_cfg_at(sprite_chunk_pos));
let required_item =
@ -1034,21 +1060,20 @@ pub fn handle_manipulate_loadout(
let (buildup_duration, use_duration, recover_duration) =
sprite_interact.durations();
update.character =
CharacterState::SpriteInteract(sprite_interact::Data {
static_data: sprite_interact::StaticData {
buildup_duration,
use_duration,
recover_duration,
sprite_pos,
sprite_kind: sprite_interact,
was_wielded: data.character.is_wield(),
was_sneak: data.character.is_stealthy(),
required_item,
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
})
update.character = CharacterState::SpriteInteract(sprite_interact::Data {
static_data: sprite_interact::StaticData {
buildup_duration,
use_duration,
recover_duration,
sprite_pos,
sprite_kind: sprite_interact,
was_wielded: data.character.is_wield(),
was_sneak: data.character.is_stealthy(),
required_item,
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
})
} else {
output_events.emit_local(LocalEvent::CreateOutcome(
Outcome::FailedSpriteUnlock { pos: sprite_pos },

View File

@ -3,7 +3,8 @@ use crate::{
comp::{fluid_dynamics::LiquidKind, tool::ToolKind},
consts::FRIC_GROUND,
lottery::LootSpec,
make_case_elim, rtsim, vol::Vox,
make_case_elim, rtsim,
vol::Vox,
};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
@ -118,13 +119,9 @@ pub struct Block {
}
impl Vox for Block {
fn empty() -> Self {
Block::empty()
}
fn empty() -> Self { Block::empty() }
fn is_empty(&self) -> bool {
self.is_air() && self.get_sprite().is_none()
}
fn is_empty(&self) -> bool { self.is_air() && self.get_sprite().is_none() }
}
impl Deref for Block {
@ -431,10 +428,7 @@ impl Block {
}
#[inline]
pub fn is_mountable(&self) -> bool {
self.get_sprite()
.map_or(false, |s| s.is_mountable())
}
pub fn is_mountable(&self) -> bool { self.get_sprite().map_or(false, |s| s.is_mountable()) }
#[inline]
pub fn is_bonkable(&self) -> bool {

View File

@ -484,29 +484,38 @@ impl SpriteKind {
z: 0.0,
};
match self {
SpriteKind::ChairSingle => &[(Vec3 {
x: 0.0,
y: 0.0,
z: 0.5,
}, UNIT_Y)],
SpriteKind::ChairDouble => &[(Vec3 {
x: -0.5,
y: 0.0,
z: 0.6,
}, UNIT_Y),
(Vec3 {
x: 0.5,
y: -0.1,
z: 0.6,
}, UNIT_Y)],
SpriteKind::ChairSingle => &[(
Vec3 {
x: 0.0,
y: 0.0,
z: 0.5,
},
UNIT_Y,
)],
SpriteKind::ChairDouble => &[
(
Vec3 {
x: -0.5,
y: 0.0,
z: 0.6,
},
UNIT_Y,
),
(
Vec3 {
x: 0.5,
y: -0.1,
z: 0.6,
},
UNIT_Y,
),
],
_ => &[],
}
}
#[inline]
pub fn is_mountable(&self) -> bool {
!self.mount_offsets().is_empty()
}
pub fn is_mountable(&self) -> bool { !self.mount_offsets().is_empty() }
/// Which tool (if any) is needed to collect this sprite?
#[inline]

View File

@ -46,9 +46,7 @@ make_case_elim!(
);
impl Default for StructureBlock {
fn default() -> Self {
StructureBlock::None
}
fn default() -> Self { StructureBlock::None }
}
#[derive(Debug)]
@ -86,7 +84,9 @@ impl assets::Compound for StructuresGroup {
.0
.iter()
.map(|sp| {
let base = cache.load::<Arc<BaseStructure<StructureBlock>>>(&sp.specifier)?.cloned();
let base = cache
.load::<Arc<BaseStructure<StructureBlock>>>(&sp.specifier)?
.cloned();
Ok(Structure {
center: Vec3::from(sp.center),
base,
@ -145,7 +145,10 @@ impl ReadVol for Structure {
}
}
pub(crate) fn load_base_structure<B: Default>(dot_vox_data: &DotVoxData, mut to_block: impl FnMut(Rgb<u8>) -> B) -> BaseStructure<B> {
pub(crate) fn load_base_structure<B: Default>(
dot_vox_data: &DotVoxData,
mut to_block: impl FnMut(Rgb<u8>) -> B,
) -> BaseStructure<B> {
let mut palette = std::array::from_fn(|_| B::default());
if let Some(model) = dot_vox_data.models.get(0) {
for (i, col) in dot_vox_data
@ -184,7 +187,9 @@ impl assets::Compound for BaseStructure<StructureBlock> {
let dot_vox_data = cache.load::<DotVoxAsset>(specifier)?.read();
let dot_vox_data = &dot_vox_data.0;
Ok(load_base_structure(dot_vox_data, |col| StructureBlock::Filled(BlockKind::Misc, col)))
Ok(load_base_structure(dot_vox_data, |col| {
StructureBlock::Filled(BlockKind::Misc, col)
}))
}
}

View File

@ -71,9 +71,10 @@ use crate::{
render::UiDrawer,
scene::camera::{self, Camera},
session::{
interactable::{BlockInteraction, Interactable},
settings_change::{
Audio, Chat as ChatChange, Interface as InterfaceChange, SettingsChange,
}, interactable::{Interactable, BlockInteraction},
},
},
settings::chat::ChatFilter,
ui::{

View File

@ -177,7 +177,7 @@ where
};
let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_fluid());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
super::terrain::should_draw_greedy(pos, delta, &|vox| {
super::terrain::should_draw_greedy(pos, delta, |vox| {
vol.get(vox)
.map(|vox| *vox)
.unwrap_or_else(|_| Block::empty())

View File

@ -147,7 +147,12 @@ pub struct Locals {
}
impl Locals {
pub fn new(model_offs: Vec3<f32>, ori: Quaternion<f32>, atlas_offs: Vec2<u32>, load_time: f32) -> Self {
pub fn new(
model_offs: Vec3<f32>,
ori: Quaternion<f32>,
atlas_offs: Vec2<u32>,
load_time: f32,
) -> Self {
let mat = Mat4::from(ori).translated_3d(model_offs);
let mat_arr = mat.into_col_arrays();

View File

@ -8,8 +8,9 @@ use crate::{
segment::{generate_mesh_base_vol_figure, generate_mesh_base_vol_terrain},
},
render::{
pipelines::terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals}, BoneMeshes, ColLightInfo,
FigureModel, Instances, Mesh, Renderer, SpriteInstance, TerrainVertex,
pipelines::terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals},
BoneMeshes, ColLightInfo, FigureModel, Instances, Mesh, Renderer, SpriteInstance,
TerrainVertex,
},
scene::{
camera::CameraMode,
@ -36,7 +37,7 @@ use core::{hash::Hash, ops::Range};
use crossbeam_utils::atomic;
use hashbrown::{hash_map::Entry, HashMap};
use serde::Deserialize;
use std::{sync::Arc, array::from_fn};
use std::{array::from_fn, sync::Arc};
use vek::*;
/// A type produced by mesh worker threads corresponding to the information
@ -321,16 +322,20 @@ where
/// the model, we don't return skeleton data.
pub fn get_model<'b>(
&'b self,
// TODO: If we ever convert to using an atlas here, use this.
// TODO: If we ever convert to using an atlas here, use this.
_col_lights: &super::FigureColLights,
body: Skel::Body,
inventory: Option<&Inventory>,
// TODO: Consider updating the tick by putting it in a Cell.
// TODO: Consider updating the tick by putting it in a Cell.
_tick: u64,
camera_mode: CameraMode,
character_state: Option<&CharacterState>,
item_key: Option<ItemKey>,
) -> Option<&<<Skel::Body as BodySpec>::ModelEntryFuture<LOD_COUNT> as ModelEntryFuture<LOD_COUNT>>::ModelEntry>{
) -> Option<
&'b <<Skel::Body as BodySpec>::ModelEntryFuture<LOD_COUNT> as ModelEntryFuture<
LOD_COUNT,
>>::ModelEntry,
> {
// TODO: Use raw entries to avoid lots of allocation (among other things).
let key = FigureKey {
body,
@ -851,10 +856,7 @@ where
}
}
pub fn get_blocks_of_interest<'c>(
&'c self,
body: Skel::Body,
) -> Option<&'c BlocksOfInterest> {
pub fn get_blocks_of_interest(&self, body: Skel::Body) -> Option<&BlocksOfInterest> {
let key = FigureKey {
body,
item_key: None,
@ -869,12 +871,12 @@ where
})
}
pub fn get_sprites<'c>(
&'c self,
pub fn get_sprites(
&self,
body: Skel::Body,
) -> Option<(
&'c BoundTerrainLocals,
&'c [Instances<SpriteInstance>; SPRITE_LOD_LEVELS],
&BoundTerrainLocals,
&[Instances<SpriteInstance>; SPRITE_LOD_LEVELS],
)> {
let key = FigureKey {
body,
@ -909,7 +911,12 @@ where
None
}
}) {
renderer.update_consts(&mut *model.terrain_locals, &[TerrainLocals::new(pos, ori, Vec2::zero(), 0.0)])
renderer.update_consts(&mut *model.terrain_locals, &[TerrainLocals::new(
pos,
ori,
Vec2::zero(),
0.0,
)])
}
}
}

View File

@ -1,4 +1,6 @@
use super::cache::{FigureKey, ToolKey, TerrainModelEntryFuture, FigureModelEntryFuture, ModelEntryFuture};
use super::cache::{
FigureKey, FigureModelEntryFuture, ModelEntryFuture, TerrainModelEntryFuture, ToolKey,
};
use common::{
assets::{self, AssetExt, AssetHandle, DotVoxAsset, ReloadWatcher, Ron},
comp::{
@ -24,7 +26,9 @@ use common::{
theropod::{self, BodyType as TBodyType, Species as TSpecies},
},
figure::{Cell, DynaUnionizer, MatCell, MatSegment, Material, Segment},
vol::{IntoFullPosIterator, ReadVol, Vox}, terrain::Block, volumes::dyna::Dyna,
terrain::Block,
vol::{IntoFullPosIterator, ReadVol, Vox},
volumes::dyna::Dyna,
};
use hashbrown::HashMap;
use serde::{Deserialize, Deserializer};
@ -5273,7 +5277,7 @@ fn mesh_ship_bone<'a, K: fmt::Debug + Eq + Hash, V, F: Fn(&V) -> Option<&'a Voxe
Some(spec) => spec,
None => {
error!("No specification exists for {:?}", obj);
return None;
},
};
@ -5283,11 +5287,11 @@ fn mesh_ship_bone<'a, K: fmt::Debug + Eq + Hash, V, F: Fn(&V) -> Option<&'a Voxe
}
impl BodySpec for ship::Body {
type BoneMesh = ShipBoneMeshes;
type Extra = ();
type Manifests = AssetHandle<Self::Spec>;
type Spec = ShipSpec;
type BoneMesh = ShipBoneMeshes;
type ModelEntryFuture<const N: usize> = TerrainModelEntryFuture<N>;
type Spec = ShipSpec;
fn load_spec() -> Result<Self::Manifests, assets::Error> { Self::Spec::load("") }

View File

@ -14,9 +14,9 @@ use crate::{
terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals},
trail, ColLights,
},
ColLightInfo, FigureBoneData, FigureDrawer, FigureLocals, FigureModel, FigureShadowDrawer,
Instances, Mesh, Quad, RenderError, Renderer, SpriteDrawer, SpriteInstance, SubModel,
TerrainVertex, CullingMode, AltIndices,
AltIndices, ColLightInfo, CullingMode, FigureBoneData, FigureDrawer, FigureLocals,
FigureModel, FigureShadowDrawer, Instances, Mesh, Quad, RenderError, Renderer,
SpriteDrawer, SpriteInstance, SubModel, TerrainVertex,
},
scene::{
camera::{Camera, CameraMode, Dependents},
@ -6312,7 +6312,7 @@ impl FigureMgr {
&'a self,
drawer: &mut SpriteDrawer<'_, 'a>,
state: &State,
focus_pos: Vec3<f32>,
cam_pos: Vec3<f32>,
sprite_render_distance: f32,
) {
span!(_guard, "render", "FigureManager::render_sprites");
@ -6322,28 +6322,48 @@ impl FigureMgr {
let sprite_hid_detail_distance = sprite_render_distance * 0.35;
let sprite_high_detail_distance = sprite_render_distance * 0.15;
for (entity, pos, body, _, inventory, scale, collider) in (
for (entity, pos, body, _, collider) in (
&ecs.entities(),
&ecs.read_storage::<Pos>(),
&ecs.read_storage::<Body>(),
ecs.read_storage::<Health>().maybe(),
ecs.read_storage::<Inventory>().maybe(),
ecs.read_storage::<Scale>().maybe(),
ecs.read_storage::<Collider>().maybe(),
)
.join()
// Don't render dead entities
.filter(|(_, _, _, health, _, _, _)| health.map_or(true, |h| !h.is_dead))
.filter(|(_, _, _, health, _)| health.map_or(true, |h| !h.is_dead))
{
if let Some((data, sprite_instances)) = self.get_sprite_instances(
entity,
body,
collider,
) {
let focus_dist_sqrd = pos.0.distance_squared(focus_pos);
if let Some((data, sprite_instances)) =
self.get_sprite_instances(entity, body, collider)
{
let dist_sqrd = cam_pos.distance_squared(pos.0);
let radius = collider.map_or(f32::INFINITY, |collider| collider.bounding_radius());
// TODO NOW: LOD
drawer.draw(data, &sprite_instances[0], &AltIndices { deep_end: 0, underground_end: 0 }, CullingMode::None)
let dist_sqrd = dist_sqrd - radius * radius;
if dist_sqrd < sprite_render_distance.powi(2) {
let lod_level = if dist_sqrd < sprite_high_detail_distance.powi(2) {
0
} else if dist_sqrd < sprite_hid_detail_distance.powi(2) {
1
} else if dist_sqrd < sprite_mid_detail_distance.powi(2) {
2
} else if dist_sqrd < sprite_low_detail_distance.powi(2) {
3
} else {
4
};
drawer.draw(
data,
&sprite_instances[lod_level],
&AltIndices {
deep_end: 0,
underground_end: 0,
},
CullingMode::None,
)
}
}
}
}
@ -6935,13 +6955,9 @@ impl FigureMgr {
entity,
mut_count: vol.mut_count,
};
self.volume_model_cache.get_sprites(
vk,
)
self.volume_model_cache.get_sprites(vk)
} else {
self.ship_model_cache.get_sprites(
*body,
)
self.ship_model_cache.get_sprites(*body)
}
},
_ => None,
@ -7157,8 +7173,12 @@ impl FigureColLights {
);
});
let terrain_locals =
renderer.create_terrain_bound_locals(&[TerrainLocals::new(pos, ori, Vec2::zero(), 0.0)]);
let terrain_locals = renderer.create_terrain_bound_locals(&[TerrainLocals::new(
pos,
ori,
Vec2::zero(),
0.0,
)]);
let sprite_instances =
sprite_instances.map(|instances| renderer.create_instances(&instances));

View File

@ -1355,7 +1355,7 @@ impl Scene {
self.figure_mgr.render_sprites(
&mut sprite_drawer,
state,
focus_pos,
cam_pos,
scene_data.sprite_render_distance,
);
self.terrain.render_sprites(

View File

@ -10,9 +10,10 @@ use crate::{
},
render::{
pipelines::{self, ColLights},
AltIndices, ColLightInfo, FirstPassDrawer, FluidVertex, GlobalModel, Instances, LodData, Mesh, Model,
RenderError, Renderer, SpriteDrawer, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex,
SpriteVerts, TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, CullingMode,
AltIndices, ColLightInfo, CullingMode, FirstPassDrawer, FluidVertex, GlobalModel,
Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteDrawer,
SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainLocals,
TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE,
},
};
@ -260,7 +261,13 @@ pub fn get_sprite_instances<'a, I: 'a>(
};
let wpos = to_wpos(rel_pos);
let seed = (wpos.x as u64).overflowing_mul(3).0.overflowing_add((wpos.y as u64).overflowing_mul(7).0).0.overflowing_add((wpos.x as u64).overflowing_mul(wpos.y as u64).0).0; // Awful PRNG
let seed = (wpos.x as u64)
.overflowing_mul(3)
.0
.overflowing_add((wpos.y as u64).overflowing_mul(7).0)
.0
.overflowing_add((wpos.x as u64).overflowing_mul(wpos.y as u64).0)
.0; // Awful PRNG
let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111;
let variation = seed as usize % cfg.variations.len();
@ -360,7 +367,9 @@ fn mesh_worker(
let mesh = mesh.as_ref().unwrap();
(&*mesh.light_map, &*mesh.glow_map)
};
let to_wpos = |rel_pos: Vec3<f32>| Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos.as_();
let to_wpos = |rel_pos: Vec3<f32>| {
Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos.as_()
};
MeshWorkerResponse {
pos,
// Extract sprite locations from volume
@ -394,7 +403,8 @@ fn mesh_worker(
(0..TerrainChunk::RECT_SIZE.x as i32)
.flat_map(|x| {
(0..TerrainChunk::RECT_SIZE.y as i32).flat_map(move |y| {
(z_bounds.0 as i32..z_bounds.1 as i32).map(move |z| Vec3::new(x, y, z).as_())
(z_bounds.0 as i32..z_bounds.1 as i32)
.map(move |z| Vec3::new(x, y, z).as_())
})
})
.filter_map(|rel_pos| Some((rel_pos, *volume.get(to_wpos(rel_pos)).ok()?))),
@ -1228,11 +1238,7 @@ impl<V: RectRasterableVol> Terrain<V> {
let sprite_instances =
response.sprite_instances.map(|(instances, alt_indices)| {
(
renderer
.create_instances(&instances),
alt_indices,
)
(renderer.create_instances(&instances), alt_indices)
});
if let Some(mesh) = response.mesh {

View File

@ -892,7 +892,8 @@ impl PlayState for SessionState {
if let Some(interactable) = &self.interactable {
match interactable {
Interactable::Block(_, pos, interaction) => {
if matches!(interaction, BlockInteraction::Mount(_)) {
if matches!(interaction, BlockInteraction::Mount(_))
{
client.mount_sprite(*pos)
}
},