mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Arbitrary volume airships
This commit is contained in:
parent
026a4a07bb
commit
46ec4203a2
@ -64,7 +64,7 @@ csv = { version = "1.1.3", optional = true }
|
||||
structopt = { version = "0.3.13", optional = true }
|
||||
# graphviz exporters
|
||||
petgraph = { version = "0.5.1", optional = true }
|
||||
# K-d trees used for RRT pathfinding
|
||||
# K-d trees used for RRT pathfinding
|
||||
kiddo = { version = "0.1", optional = true }
|
||||
|
||||
# Data structures
|
||||
|
@ -107,6 +107,7 @@ pub enum ChatCommand {
|
||||
Whitelist,
|
||||
Wiring,
|
||||
World,
|
||||
MakeVolume,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
@ -638,6 +639,7 @@ impl ChatCommand {
|
||||
"Send messages to everyone on the server",
|
||||
None,
|
||||
),
|
||||
ChatCommand::MakeVolume => cmd(vec![], "Create a volume (experimental)", Some(Admin)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -709,6 +711,7 @@ impl ChatCommand {
|
||||
ChatCommand::Wiring => "wiring",
|
||||
ChatCommand::Whitelist => "whitelist",
|
||||
ChatCommand::World => "world",
|
||||
ChatCommand::MakeVolume => "make_volume",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -859,6 +859,7 @@ impl Body {
|
||||
ship::Body::AirBalloon => [0.0, 0.0, 5.0],
|
||||
ship::Body::SailBoat => [-2.0, -5.0, 4.0],
|
||||
ship::Body::Galleon => [-2.0, -5.0, 4.0],
|
||||
ship::Body::Volume => [0.0, 0.0, 0.0],
|
||||
},
|
||||
_ => [0.0, 0.0, 0.0],
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
comp::{Density, Mass},
|
||||
comp::{Collider, Density, Mass},
|
||||
consts::{AIR_DENSITY, WATER_DENSITY},
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
volumes::dyna::Dyna,
|
||||
};
|
||||
use rand::prelude::SliceRandom;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::Vec3;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
pub const ALL_BODIES: [Body; 4] = [
|
||||
Body::DefaultAirship,
|
||||
@ -23,6 +26,7 @@ make_case_elim!(
|
||||
AirBalloon = 1,
|
||||
SailBoat = 2,
|
||||
Galleon = 3,
|
||||
Volume = 4,
|
||||
}
|
||||
);
|
||||
|
||||
@ -38,18 +42,21 @@ impl Body {
|
||||
|
||||
pub fn random_with(rng: &mut impl rand::Rng) -> Self { *(&ALL_BODIES).choose(rng).unwrap() }
|
||||
|
||||
pub fn manifest_entry(&self) -> &'static str {
|
||||
/// Return the structure manifest that this ship uses. `None` means that it
|
||||
/// should be derived from the collider.
|
||||
pub fn manifest_entry(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
Body::DefaultAirship => "airship_human.structure",
|
||||
Body::AirBalloon => "air_balloon.structure",
|
||||
Body::SailBoat => "sail_boat.structure",
|
||||
Body::Galleon => "galleon.structure",
|
||||
Body::DefaultAirship => Some("airship_human.structure"),
|
||||
Body::AirBalloon => Some("air_balloon.structure"),
|
||||
Body::SailBoat => Some("sail_boat.structure"),
|
||||
Body::Galleon => Some("galleon.structure"),
|
||||
Body::Volume => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dimensions(&self) -> Vec3<f32> {
|
||||
match self {
|
||||
Body::DefaultAirship => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::DefaultAirship | Body::Volume => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::AirBalloon => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::SailBoat => Vec3::new(13.0, 31.0, 3.0),
|
||||
Body::Galleon => Vec3::new(13.0, 32.0, 3.0),
|
||||
@ -58,7 +65,7 @@ impl Body {
|
||||
|
||||
fn balloon_vol(&self) -> f32 {
|
||||
match self {
|
||||
Body::DefaultAirship | Body::AirBalloon => {
|
||||
Body::DefaultAirship | Body::AirBalloon | Body::Volume => {
|
||||
let spheroid_vol = |equat_d: f32, polar_d: f32| -> f32 {
|
||||
(std::f32::consts::PI / 6.0) * equat_d.powi(2) * polar_d
|
||||
};
|
||||
@ -84,18 +91,39 @@ impl Body {
|
||||
|
||||
pub fn density(&self) -> Density {
|
||||
match self {
|
||||
Body::DefaultAirship | Body::AirBalloon => Density(AIR_DENSITY),
|
||||
Body::DefaultAirship | Body::AirBalloon | Body::Volume => Density(AIR_DENSITY),
|
||||
_ => Density(AIR_DENSITY * 0.8 + WATER_DENSITY * 0.2), // Most boats should be buoyant
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mass(&self) -> Mass { Mass((self.hull_vol() + self.balloon_vol()) * self.density().0) }
|
||||
|
||||
pub fn can_fly(&self) -> bool { matches!(self, Body::DefaultAirship | Body::AirBalloon) }
|
||||
pub fn can_fly(&self) -> bool {
|
||||
matches!(self, Body::DefaultAirship | Body::AirBalloon | Body::Volume)
|
||||
}
|
||||
|
||||
pub fn has_water_thrust(&self) -> bool {
|
||||
!self.can_fly() // TODO: Differentiate this more carefully
|
||||
}
|
||||
|
||||
pub fn make_collider(&self) -> Collider {
|
||||
match self.manifest_entry() {
|
||||
Some(manifest_entry) => Collider::Voxel {
|
||||
id: manifest_entry.to_string(),
|
||||
},
|
||||
None => {
|
||||
use rand::prelude::*;
|
||||
let sz = Vec3::broadcast(11);
|
||||
Collider::Volume(Arc::new(figuredata::VoxelCollider::from_fn(sz, |_pos| {
|
||||
if thread_rng().gen_bool(0.25) {
|
||||
Block::new(BlockKind::Rock, Rgb::new(255, 0, 0))
|
||||
} else {
|
||||
Block::air(SpriteKind::Empty)
|
||||
}
|
||||
})))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Terrain is 11.0 scale relative to small-scale voxels,
|
||||
@ -117,7 +145,7 @@ pub mod figuredata {
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::Vec3;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -148,10 +176,25 @@ pub mod figuredata {
|
||||
pub colliders: HashMap<String, VoxelCollider>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct VoxelCollider {
|
||||
pub dyna: Dyna<Block, (), ColumnAccess>,
|
||||
pub(super) dyna: Dyna<Block, (), ColumnAccess>,
|
||||
pub translation: Vec3<f32>,
|
||||
/// This value should be incremented every time the volume is mutated
|
||||
/// and can be used to keep track of volume changes.
|
||||
pub mut_count: usize,
|
||||
}
|
||||
|
||||
impl VoxelCollider {
|
||||
pub fn from_fn<F: FnMut(Vec3<i32>) -> Block>(sz: Vec3<u32>, f: F) -> Self {
|
||||
Self {
|
||||
dyna: Dyna::from_fn(sz, (), f),
|
||||
translation: -sz.map(|e| e as f32) / 2.0,
|
||||
mut_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn volume(&self) -> &Dyna<Block, (), ColumnAccess> { &self.dyna }
|
||||
}
|
||||
|
||||
impl assets::Compound for ShipSpec {
|
||||
@ -180,6 +223,7 @@ pub mod figuredata {
|
||||
let collider = VoxelCollider {
|
||||
dyna,
|
||||
translation: Vec3::from(bone.offset) + Vec3::from(bone.phys_offset),
|
||||
mut_count: 0,
|
||||
};
|
||||
colliders.insert(bone.central.0.clone(), collider);
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
use super::{Fluid, Ori};
|
||||
use crate::{consts::WATER_DENSITY, terrain::Block, uid::Uid};
|
||||
use crate::{
|
||||
comp::body::ship::figuredata::VoxelCollider, consts::WATER_DENSITY, terrain::Block, uid::Uid,
|
||||
};
|
||||
use hashbrown::HashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, DerefFlaggedStorage, NullStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
/// Position
|
||||
@ -103,13 +106,16 @@ impl Component for Density {
|
||||
}
|
||||
|
||||
// Collider
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum Collider {
|
||||
/// A volume based on an existing voxel asset.
|
||||
// TODO: pass the map from ids -> voxel data to get_radius
|
||||
// and get_z_limits to compute a bounding cylinder.
|
||||
Voxel {
|
||||
id: String,
|
||||
},
|
||||
/// A mutable volume.
|
||||
Volume(Arc<VoxelCollider>),
|
||||
/// Capsule prism with line segment from p0 to p1
|
||||
CapsulePrism {
|
||||
p0: Vec2<f32>,
|
||||
@ -122,9 +128,11 @@ pub enum Collider {
|
||||
}
|
||||
|
||||
impl Collider {
|
||||
pub fn is_voxel(&self) -> bool { matches!(self, Collider::Voxel { .. } | Collider::Volume(_)) }
|
||||
|
||||
pub fn bounding_radius(&self) -> f32 {
|
||||
match self {
|
||||
Collider::Voxel { .. } => 1.0,
|
||||
Collider::Voxel { .. } | Collider::Volume(_) => 1.0,
|
||||
Collider::CapsulePrism { radius, p0, p1, .. } => {
|
||||
let a = p0.distance(*p1);
|
||||
a / 2.0 + *radius
|
||||
@ -140,7 +148,7 @@ impl Collider {
|
||||
|
||||
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
||||
match self {
|
||||
Collider::Voxel { .. } => (0.0, 1.0),
|
||||
Collider::Voxel { .. } | Collider::Volume(_) => (0.0, 1.0),
|
||||
Collider::CapsulePrism { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
||||
Collider::Point => (0.0, 0.0),
|
||||
}
|
||||
|
@ -129,6 +129,19 @@ impl<V: Clone, M, A: Access> Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`Dyna::filled`], but with the voxel determined by the function
|
||||
/// `f`.
|
||||
pub fn from_fn<F: FnMut(Vec3<i32>) -> V>(sz: Vec3<u32>, meta: M, mut f: F) -> Self {
|
||||
Self {
|
||||
vox: (0..sz.product() as usize)
|
||||
.map(|idx| f(A::pos(idx, sz)))
|
||||
.collect(),
|
||||
meta,
|
||||
sz,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the internal metadata.
|
||||
pub fn metadata(&self) -> &M { &self.meta }
|
||||
|
||||
@ -138,6 +151,8 @@ impl<V: Clone, M, A: Access> Dyna<V, M, A> {
|
||||
|
||||
pub trait Access {
|
||||
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize;
|
||||
/// `idx` must be in range, permitted to panic otherwise.
|
||||
fn pos(idx: usize, sz: Vec3<u32>) -> Vec3<i32>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -147,4 +162,11 @@ impl Access for ColumnAccess {
|
||||
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize {
|
||||
(pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize
|
||||
}
|
||||
|
||||
fn pos(idx: usize, sz: Vec3<u32>) -> Vec3<i32> {
|
||||
let z = idx as u32 % sz.z;
|
||||
let y = (idx as u32 / sz.z) % sz.y;
|
||||
let x = idx as u32 / (sz.y * sz.z);
|
||||
Vec3::new(x, y, z).map(|e| e as i32)
|
||||
}
|
||||
}
|
||||
|
@ -224,8 +224,15 @@ impl<'a> PhysicsData<'a> {
|
||||
phys_cache.scaled_radius = flat_radius;
|
||||
|
||||
let neighborhood_radius = match collider {
|
||||
<<<<<<< HEAD
|
||||
Collider::CapsulePrism { radius, .. } => radius * scale,
|
||||
Collider::Voxel { .. } | Collider::Point => flat_radius,
|
||||
=======
|
||||
Some(Collider::CapsulePrism { radius, .. }) => radius * scale,
|
||||
Some(Collider::Voxel { .. } | Collider::Volume(_) | Collider::Point) | None => {
|
||||
flat_radius
|
||||
},
|
||||
>>>>>>> 51f014dd3 (Arbitrary volume airships)
|
||||
};
|
||||
phys_cache.neighborhood_radius = neighborhood_radius;
|
||||
|
||||
@ -265,7 +272,11 @@ impl<'a> PhysicsData<'a> {
|
||||
Some((p0, p1))
|
||||
}
|
||||
},
|
||||
<<<<<<< HEAD
|
||||
Collider::Voxel { .. } | Collider::Point => None,
|
||||
=======
|
||||
Some(Collider::Voxel { .. } | Collider::Volume(_) | Collider::Point) | None => None,
|
||||
>>>>>>> 51f014dd3 (Arbitrary volume airships)
|
||||
};
|
||||
phys_cache.origins = origins;
|
||||
phys_cache.ori = ori;
|
||||
@ -527,13 +538,15 @@ impl<'a> PhysicsData<'a> {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let voxel_id = match collider {
|
||||
Collider::Voxel { id } => id,
|
||||
_ => continue,
|
||||
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||
let vol = match collider {
|
||||
Collider::Voxel { id } => voxel_colliders_manifest.colliders.get(&*id),
|
||||
Collider::Volume(vol) => Some(&**vol),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(voxel_collider) = VOXEL_COLLIDER_MANIFEST.read().colliders.get(&*voxel_id) {
|
||||
let sphere = voxel_collider_bounding_sphere(voxel_collider, pos, ori);
|
||||
if let Some(vol) = vol {
|
||||
let sphere = voxel_collider_bounding_sphere(vol, pos, ori);
|
||||
let radius = sphere.radius.ceil() as u32;
|
||||
let pos_2d = sphere.center.xy().map(|e| e as i32);
|
||||
const POS_TRUNCATION_ERROR: u32 = 1;
|
||||
@ -729,7 +742,7 @@ impl<'a> PhysicsData<'a> {
|
||||
!&read.mountings,
|
||||
)
|
||||
.par_join()
|
||||
.filter(|tuple| matches!(tuple.3, Collider::Voxel { .. }) == terrain_like_entities)
|
||||
.filter(|tuple| tuple.3.is_voxel() == terrain_like_entities)
|
||||
.map_init(
|
||||
|| {
|
||||
prof_span!(guard, "physics e<>t rayon job");
|
||||
@ -760,7 +773,7 @@ impl<'a> PhysicsData<'a> {
|
||||
let old_ori = *ori;
|
||||
let mut ori = *ori;
|
||||
|
||||
let scale = if let Collider::Voxel { .. } = collider {
|
||||
let scale = if collider.is_voxel() {
|
||||
scale.map(|s| s.0).unwrap_or(1.0)
|
||||
} else {
|
||||
// TODO: Use scale & actual proportions when pathfinding is good
|
||||
@ -806,7 +819,7 @@ impl<'a> PhysicsData<'a> {
|
||||
character_state.map_or(false, |cs| matches!(cs, CharacterState::Climb(_)));
|
||||
|
||||
match &collider {
|
||||
Collider::Voxel { .. } => {
|
||||
Collider::Voxel { .. } | Collider::Volume(_) => {
|
||||
// For now, treat entities with voxel colliders
|
||||
// as their bounding cylinders for the purposes of
|
||||
// colliding them with terrain.
|
||||
@ -1029,10 +1042,13 @@ impl<'a> PhysicsData<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
let voxel_id = if let Collider::Voxel { id } = collider_other {
|
||||
id
|
||||
} else {
|
||||
return;
|
||||
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||
let voxel_collider = match collider_other {
|
||||
Collider::Voxel { id } => {
|
||||
voxel_colliders_manifest.colliders.get(id)
|
||||
},
|
||||
Collider::Volume(vol) => Some(&**vol),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// use bounding cylinder regardless of our collider
|
||||
@ -1045,9 +1061,7 @@ impl<'a> PhysicsData<'a> {
|
||||
let z_min = 0.0;
|
||||
let z_max = z_max.clamped(1.2, 1.95) * scale;
|
||||
|
||||
if let Some(voxel_collider) =
|
||||
VOXEL_COLLIDER_MANIFEST.read().colliders.get(voxel_id)
|
||||
{
|
||||
if let Some(voxel_collider) = voxel_collider {
|
||||
// TODO: cache/precompute sphere?
|
||||
let voxel_sphere = voxel_collider_bounding_sphere(
|
||||
voxel_collider,
|
||||
@ -1112,7 +1126,7 @@ impl<'a> PhysicsData<'a> {
|
||||
let cylinder = (radius, z_min, z_max);
|
||||
box_voxel_collision(
|
||||
cylinder,
|
||||
&voxel_collider.dyna,
|
||||
&voxel_collider.volume(),
|
||||
entity,
|
||||
&mut cpos,
|
||||
transform_to.mul_point(tgt_pos - wpos),
|
||||
@ -1226,7 +1240,7 @@ impl<'a> PhysicsData<'a> {
|
||||
&read.colliders,
|
||||
)
|
||||
.join()
|
||||
.filter(|tuple| matches!(tuple.5, Collider::Voxel { .. }) == terrain_like_entities)
|
||||
.filter(|tuple| tuple.5.is_voxel() == terrain_like_entities)
|
||||
{
|
||||
if let Some(new_pos) = pos_vel_ori_defer.pos.take() {
|
||||
*pos = new_pos;
|
||||
@ -1725,8 +1739,8 @@ fn voxel_collider_bounding_sphere(
|
||||
) -> Sphere<f32, f32> {
|
||||
let origin_offset = voxel_collider.translation;
|
||||
use common::vol::SizedVol;
|
||||
let lower_bound = voxel_collider.dyna.lower_bound().map(|e| e as f32);
|
||||
let upper_bound = voxel_collider.dyna.upper_bound().map(|e| e as f32);
|
||||
let lower_bound = voxel_collider.volume().lower_bound().map(|e| e as f32);
|
||||
let upper_bound = voxel_collider.volume().upper_bound().map(|e| e as f32);
|
||||
let center = (lower_bound + upper_bound) / 2.0;
|
||||
// Compute vector from the origin (where pos value corresponds to) and the model
|
||||
// center
|
||||
@ -1845,8 +1859,8 @@ fn resolve_e2e_collision(
|
||||
&& (!is_sticky || is_mid_air)
|
||||
&& diff.magnitude_squared() > 0.0
|
||||
&& !is_projectile
|
||||
&& !matches!(collider_other, Collider::Voxel { .. })
|
||||
&& !matches!(collider, Collider::Voxel { .. })
|
||||
&& !collider_other.map_or(false, |c| c.is_voxel())
|
||||
&& !collider.map_or(false, |c| c.is_voxel())
|
||||
{
|
||||
const ELASTIC_FORCE_COEFFICIENT: f32 = 400.0;
|
||||
let mass_coefficient = mass_other.0 / (mass.0 + mass_other.0);
|
||||
|
@ -35,9 +35,9 @@ use common::{
|
||||
generation::EntityInfo,
|
||||
npc::{self, get_npc_name},
|
||||
resources::{BattleMode, PlayerPhysicsSettings, Time, TimeOfDay},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize, TerrainGrid},
|
||||
uid::Uid,
|
||||
vol::RectVolSize,
|
||||
vol::{ReadVol, RectVolSize},
|
||||
Damage, DamageKind, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
|
||||
};
|
||||
use common_net::{
|
||||
@ -50,7 +50,7 @@ use hashbrown::{HashMap, HashSet};
|
||||
use humantime::Duration as HumanDuration;
|
||||
use rand::Rng;
|
||||
use specs::{storage::StorageEntry, Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
use std::str::FromStr;
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
use vek::*;
|
||||
use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement};
|
||||
use world::util::Sampler;
|
||||
@ -176,6 +176,7 @@ fn do_command(
|
||||
ChatCommand::Wiring => handle_spawn_wiring,
|
||||
ChatCommand::Whitelist => handle_whitelist,
|
||||
ChatCommand::World => handle_world,
|
||||
ChatCommand::MakeVolume => handle_make_volume,
|
||||
};
|
||||
|
||||
handler(server, client, target, args, cmd)
|
||||
@ -1269,7 +1270,7 @@ fn handle_spawn_airship(
|
||||
let ship = comp::ship::Body::random();
|
||||
let mut builder = server
|
||||
.state
|
||||
.create_ship(pos, ship, true)
|
||||
.create_ship(pos, ship, |ship| ship.make_collider(), true)
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.65, 0.2),
|
||||
strength: 2.0,
|
||||
@ -1293,6 +1294,47 @@ fn handle_spawn_airship(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_make_volume(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
_args: Vec<String>,
|
||||
_action: &ChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
use comp::body::ship::figuredata::VoxelCollider;
|
||||
use rand::prelude::*;
|
||||
|
||||
//let () = parse_args!(args);
|
||||
let mut pos = position(server, target, "target")?;
|
||||
let ship = comp::ship::Body::Volume;
|
||||
let sz = Vec3::new(15, 15, 15);
|
||||
let collider = {
|
||||
let terrain = server.state().terrain();
|
||||
comp::Collider::Volume(Arc::new(VoxelCollider::from_fn(sz, |rpos| {
|
||||
terrain
|
||||
.get(pos.0.map(|e| e.floor() as i32) + rpos - sz.map(|e| e as i32) / 2)
|
||||
.ok()
|
||||
.copied()
|
||||
.unwrap_or_else(Block::empty)
|
||||
})))
|
||||
};
|
||||
server
|
||||
.state
|
||||
.create_ship(
|
||||
comp::Pos(pos.0 + Vec3::unit_z() * 50.0),
|
||||
ship,
|
||||
move |_| collider,
|
||||
true,
|
||||
)
|
||||
.build();
|
||||
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerGeneral::server_msg(ChatType::CommandInfo, "Created a volume"),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_spawn_campfire(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
|
@ -156,7 +156,9 @@ pub fn handle_create_ship(
|
||||
agent: Option<Agent>,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
) {
|
||||
let mut entity = server.state.create_ship(pos, ship, mountable);
|
||||
let mut entity = server
|
||||
.state
|
||||
.create_ship(pos, ship, |ship| ship.make_collider(), mountable);
|
||||
if let Some(mut agent) = agent {
|
||||
let (kp, ki, kd) = pid_coefficients(&Body::Ship(ship));
|
||||
fn pure_z(sp: Vec3<f32>, pv: Vec3<f32>) -> f32 { (sp - pv).z }
|
||||
|
@ -51,10 +51,11 @@ pub trait StateExt {
|
||||
) -> EcsEntityBuilder;
|
||||
/// Build a static object entity
|
||||
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
|
||||
fn create_ship(
|
||||
fn create_ship<F: FnOnce(comp::ship::Body) -> comp::Collider>(
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
ship: comp::ship::Body,
|
||||
make_collider: F,
|
||||
mountable: bool,
|
||||
) -> EcsEntityBuilder;
|
||||
/// Build a projectile
|
||||
@ -200,9 +201,7 @@ impl StateExt for State {
|
||||
.with(body.mass())
|
||||
.with(body.density())
|
||||
.with(match body {
|
||||
comp::Body::Ship(ship) => comp::Collider::Voxel {
|
||||
id: ship.manifest_entry().to_string(),
|
||||
},
|
||||
comp::Body::Ship(ship) => ship.make_collider(),
|
||||
_ => capsule(&body),
|
||||
})
|
||||
.with(comp::Controller::default())
|
||||
@ -243,10 +242,11 @@ impl StateExt for State {
|
||||
.with(body)
|
||||
}
|
||||
|
||||
fn create_ship(
|
||||
fn create_ship<F: FnOnce(comp::ship::Body) -> comp::Collider>(
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
ship: comp::ship::Body,
|
||||
make_collider: F,
|
||||
mountable: bool,
|
||||
) -> EcsEntityBuilder {
|
||||
let body = comp::Body::Ship(ship);
|
||||
@ -258,9 +258,7 @@ impl StateExt for State {
|
||||
.with(comp::Ori::default())
|
||||
.with(body.mass())
|
||||
.with(body.density())
|
||||
.with(comp::Collider::Voxel {
|
||||
id: ship.manifest_entry().to_string(),
|
||||
})
|
||||
.with(make_collider(ship))
|
||||
.with(body)
|
||||
.with(comp::Scale(comp::ship::AIRSHIP_SCALE))
|
||||
.with(comp::Controller::default())
|
||||
|
@ -81,7 +81,7 @@ pub struct FigureBoneData(pub MatRaw, pub MatRaw);
|
||||
|
||||
pub const MAX_BONE_COUNT: usize = 16;
|
||||
|
||||
fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
|
||||
pub fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
|
||||
let normal = mat.map_cols(Vec4::normalized);
|
||||
FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
|
||||
}
|
||||
|
@ -95,24 +95,28 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
AirBalloon => (0.0, 0.0, 0.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
Galleon => (0.0, 0.0, 0.0),
|
||||
Volume => (0.0, 0.0, 0.0),
|
||||
},
|
||||
bone1: match body {
|
||||
DefaultAirship => (-13.0, -25.0, 10.0),
|
||||
AirBalloon => (0.0, 0.0, 0.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
Galleon => (0.0, 0.0, 0.0),
|
||||
Volume => (0.0, 0.0, 0.0),
|
||||
},
|
||||
bone2: match body {
|
||||
DefaultAirship => (13.0, -25.0, 10.0),
|
||||
AirBalloon => (0.0, 0.0, 0.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
Galleon => (0.0, 0.0, 0.0),
|
||||
Volume => (0.0, 0.0, 0.0),
|
||||
},
|
||||
bone3: match body {
|
||||
DefaultAirship => (0.0, -27.5, 8.5),
|
||||
AirBalloon => (0.0, -9.0, 8.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
Galleon => (0.0, 0.0, 0.0),
|
||||
Volume => (0.0, 0.0, 0.0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ use crate::{
|
||||
};
|
||||
use anim::Skeleton;
|
||||
use common::{
|
||||
assets::AssetHandle,
|
||||
comp::{
|
||||
inventory::{
|
||||
slot::{ArmorSlot, EquipSlot},
|
||||
@ -287,7 +286,7 @@ where
|
||||
Skel::Body: BodySpec,
|
||||
{
|
||||
models: HashMap<FigureKey<Skel::Body>, ((FigureModelEntryFuture<LOD_COUNT>, Skel::Attr), u64)>,
|
||||
manifests: AssetHandle<<Skel::Body as BodySpec>::Spec>,
|
||||
manifests: <Skel::Body as BodySpec>::Manifests,
|
||||
}
|
||||
|
||||
impl<Skel: Skeleton> FigureModelCache<Skel>
|
||||
@ -345,6 +344,7 @@ where
|
||||
col_lights: &mut super::FigureColLights,
|
||||
body: Skel::Body,
|
||||
inventory: Option<&Inventory>,
|
||||
extra: <Skel::Body as BodySpec>::Extra,
|
||||
tick: u64,
|
||||
camera_mode: CameraMode,
|
||||
character_state: Option<&CharacterState>,
|
||||
@ -407,13 +407,12 @@ where
|
||||
Entry::Vacant(v) => {
|
||||
let key = v.key().clone();
|
||||
let slot = Arc::new(atomic::AtomicCell::new(None));
|
||||
let manifests = self.manifests;
|
||||
let manifests = self.manifests.clone();
|
||||
let slot_ = Arc::clone(&slot);
|
||||
|
||||
slow_jobs.spawn("FIGURE_MESHING", move || {
|
||||
// First, load all the base vertex data.
|
||||
let manifests = &*manifests.read();
|
||||
let meshes = <Skel::Body as BodySpec>::bone_meshes(&key, manifests);
|
||||
let meshes = <Skel::Body as BodySpec>::bone_meshes(&key, &manifests, extra);
|
||||
|
||||
// Then, set up meshing context.
|
||||
let mut greedy = FigureModel::make_greedy();
|
||||
@ -562,7 +561,7 @@ where
|
||||
{
|
||||
// Check for reloaded manifests
|
||||
// TODO: maybe do this in a different function, maintain?
|
||||
if self.manifests.reloaded() {
|
||||
if <Skel::Body as BodySpec>::is_reloaded(&mut self.manifests) {
|
||||
col_lights.atlas.clear();
|
||||
self.models.clear();
|
||||
}
|
||||
|
@ -87,9 +87,14 @@ fn recolor_grey(rgb: Rgb<u8>, color: Rgb<u8>) -> Rgb<u8> {
|
||||
/// A set of reloadable specifications for a Body.
|
||||
pub trait BodySpec: Sized {
|
||||
type Spec;
|
||||
type Manifests: Send + Sync + Clone;
|
||||
type Extra: Send + Sync;
|
||||
|
||||
/// Initialize all the specifications for this Body.
|
||||
fn load_spec() -> Result<AssetHandle<Self::Spec>, assets::Error>;
|
||||
fn load_spec() -> Result<Self::Manifests, assets::Error>;
|
||||
|
||||
/// Determine whether the cache's manifest was reloaded
|
||||
fn is_reloaded(manifests: &mut Self::Manifests) -> bool;
|
||||
|
||||
/// Mesh bones using the given spec, character state, and mesh generation
|
||||
/// function.
|
||||
@ -100,7 +105,8 @@ pub trait BodySpec: Sized {
|
||||
/// in which case this strategy might change.
|
||||
fn bone_meshes(
|
||||
key: &FigureKey<Self>,
|
||||
spec: &Self::Spec,
|
||||
manifests: &Self::Manifests,
|
||||
extra: Self::Extra,
|
||||
) -> [Option<BoneMeshes>; anim::MAX_BONE_COUNT];
|
||||
}
|
||||
|
||||
@ -125,16 +131,22 @@ macro_rules! make_vox_spec {
|
||||
|
||||
impl BodySpec for $body {
|
||||
type Spec = $Spec;
|
||||
type Manifests = AssetHandle<Self::Spec>;
|
||||
type Extra = ();
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn load_spec() -> Result<AssetHandle<Self::Spec>, assets::Error> {
|
||||
fn load_spec() -> Result<Self::Manifests, assets::Error> {
|
||||
Self::Spec::load("")
|
||||
}
|
||||
|
||||
fn is_reloaded(manifests: &mut Self::Manifests) -> bool { manifests.reloaded() }
|
||||
|
||||
fn bone_meshes(
|
||||
$self_pat: &FigureKey<Self>,
|
||||
$spec_pat: &Self::Spec,
|
||||
manifests: &Self::Manifests,
|
||||
_: Self::Extra,
|
||||
) -> [Option<BoneMeshes>; anim::MAX_BONE_COUNT] {
|
||||
let $spec_pat = &*manifests.read();
|
||||
$bone_meshes
|
||||
}
|
||||
}
|
||||
@ -4528,15 +4540,21 @@ fn mesh_ship_bone<K: fmt::Debug + Eq + Hash, V, F: Fn(&V) -> &ShipCentralSubSpec
|
||||
}
|
||||
|
||||
impl BodySpec for ship::Body {
|
||||
type Extra = ();
|
||||
type Manifests = AssetHandle<Self::Spec>;
|
||||
type Spec = ShipSpec;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn load_spec() -> Result<AssetHandle<Self::Spec>, assets::Error> { Self::Spec::load("") }
|
||||
fn load_spec() -> Result<Self::Manifests, assets::Error> { Self::Spec::load("") }
|
||||
|
||||
fn is_reloaded(manifests: &mut Self::Manifests) -> bool { manifests.reloaded() }
|
||||
|
||||
fn bone_meshes(
|
||||
FigureKey { body, .. }: &FigureKey<Self>,
|
||||
spec: &Self::Spec,
|
||||
manifests: &Self::Manifests,
|
||||
_: Self::Extra,
|
||||
) -> [Option<BoneMeshes>; anim::MAX_BONE_COUNT] {
|
||||
let spec = &*manifests.read();
|
||||
let map = &(spec.central.read().0).0;
|
||||
[
|
||||
Some(mesh_ship_bone(map, body, |spec| &spec.bone0)),
|
||||
|
@ -1,8 +1,10 @@
|
||||
mod cache;
|
||||
pub mod load;
|
||||
mod volume;
|
||||
|
||||
pub use cache::FigureModelCache;
|
||||
pub use load::load_mesh; // TODO: Don't make this public.
|
||||
pub use volume::VolumeKey;
|
||||
|
||||
use crate::{
|
||||
ecs::comp::Interpolated,
|
||||
@ -30,7 +32,7 @@ use common::{
|
||||
comp::{
|
||||
inventory::slot::EquipSlot,
|
||||
item::{Hands, ItemKind, ToolKind},
|
||||
Body, CharacterState, Controller, Health, Inventory, Item, Last, LightAnimation,
|
||||
Body, CharacterState, Collider, Controller, Health, Inventory, Item, Last, LightAnimation,
|
||||
LightEmitter, Mounting, Ori, PhysicsState, PoiseState, Pos, Scale, Vel,
|
||||
},
|
||||
resources::DeltaTime,
|
||||
@ -113,6 +115,7 @@ struct FigureMgrStates {
|
||||
golem_states: HashMap<EcsEntity, FigureState<GolemSkeleton>>,
|
||||
object_states: HashMap<EcsEntity, FigureState<ObjectSkeleton>>,
|
||||
ship_states: HashMap<EcsEntity, FigureState<ShipSkeleton>>,
|
||||
volume_states: HashMap<EcsEntity, FigureState<VolumeKey>>,
|
||||
}
|
||||
|
||||
impl FigureMgrStates {
|
||||
@ -133,6 +136,7 @@ impl FigureMgrStates {
|
||||
golem_states: HashMap::new(),
|
||||
object_states: HashMap::new(),
|
||||
ship_states: HashMap::new(),
|
||||
volume_states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +197,13 @@ impl FigureMgrStates {
|
||||
.map(DerefMut::deref_mut),
|
||||
Body::Golem(_) => self.golem_states.get_mut(entity).map(DerefMut::deref_mut),
|
||||
Body::Object(_) => self.object_states.get_mut(entity).map(DerefMut::deref_mut),
|
||||
Body::Ship(_) => self.ship_states.get_mut(entity).map(DerefMut::deref_mut),
|
||||
Body::Ship(ship) => {
|
||||
if ship.manifest_entry().is_some() {
|
||||
self.ship_states.get_mut(entity).map(DerefMut::deref_mut)
|
||||
} else {
|
||||
self.volume_states.get_mut(entity).map(DerefMut::deref_mut)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +227,13 @@ impl FigureMgrStates {
|
||||
Body::BipedSmall(_) => self.biped_small_states.remove(entity).map(|e| e.meta),
|
||||
Body::Golem(_) => self.golem_states.remove(entity).map(|e| e.meta),
|
||||
Body::Object(_) => self.object_states.remove(entity).map(|e| e.meta),
|
||||
Body::Ship(_) => self.ship_states.remove(entity).map(|e| e.meta),
|
||||
Body::Ship(ship) => {
|
||||
if ship.manifest_entry().is_some() {
|
||||
self.ship_states.remove(entity).map(|e| e.meta)
|
||||
} else {
|
||||
self.volume_states.remove(entity).map(|e| e.meta)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,6 +254,7 @@ impl FigureMgrStates {
|
||||
self.golem_states.retain(|k, v| f(k, &mut *v));
|
||||
self.object_states.retain(|k, v| f(k, &mut *v));
|
||||
self.ship_states.retain(|k, v| f(k, &mut *v));
|
||||
self.volume_states.retain(|k, v| f(k, &mut *v));
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
@ -257,6 +274,7 @@ impl FigureMgrStates {
|
||||
+ self.golem_states.len()
|
||||
+ self.object_states.len()
|
||||
+ self.ship_states.len()
|
||||
+ self.volume_states.len()
|
||||
}
|
||||
|
||||
fn count_visible(&self) -> usize {
|
||||
@ -330,6 +348,11 @@ impl FigureMgrStates {
|
||||
.filter(|(_, c)| c.visible())
|
||||
.count()
|
||||
+ self.ship_states.iter().filter(|(_, c)| c.visible()).count()
|
||||
+ self
|
||||
.volume_states
|
||||
.iter()
|
||||
.filter(|(_, c)| c.visible())
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,6 +373,7 @@ pub struct FigureMgr {
|
||||
object_model_cache: FigureModelCache<ObjectSkeleton>,
|
||||
ship_model_cache: FigureModelCache<ShipSkeleton>,
|
||||
golem_model_cache: FigureModelCache<GolemSkeleton>,
|
||||
volume_model_cache: FigureModelCache<VolumeKey>,
|
||||
states: FigureMgrStates,
|
||||
}
|
||||
|
||||
@ -372,6 +396,7 @@ impl FigureMgr {
|
||||
object_model_cache: FigureModelCache::new(),
|
||||
ship_model_cache: FigureModelCache::new(),
|
||||
golem_model_cache: FigureModelCache::new(),
|
||||
volume_model_cache: FigureModelCache::new(),
|
||||
states: FigureMgrStates::default(),
|
||||
}
|
||||
}
|
||||
@ -404,6 +429,7 @@ impl FigureMgr {
|
||||
self.object_model_cache.clean(&mut self.col_lights, tick);
|
||||
self.ship_model_cache.clean(&mut self.col_lights, tick);
|
||||
self.golem_model_cache.clean(&mut self.col_lights, tick);
|
||||
self.volume_model_cache.clean(&mut self.col_lights, tick);
|
||||
}
|
||||
|
||||
pub fn update_lighting(&mut self, scene_data: &SceneData) {
|
||||
@ -600,6 +626,7 @@ impl FigureMgr {
|
||||
item,
|
||||
light_emitter,
|
||||
mountings,
|
||||
collider,
|
||||
),
|
||||
) in (
|
||||
&ecs.entities(),
|
||||
@ -617,6 +644,7 @@ impl FigureMgr {
|
||||
ecs.read_storage::<Item>().maybe(),
|
||||
ecs.read_storage::<LightEmitter>().maybe(),
|
||||
ecs.read_storage::<Mounting>().maybe(),
|
||||
ecs.read_storage::<Collider>().maybe(),
|
||||
)
|
||||
.join()
|
||||
.enumerate()
|
||||
@ -794,6 +822,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -1670,6 +1699,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -1859,6 +1889,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -2173,6 +2204,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -2519,6 +2551,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -2620,6 +2653,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -2700,6 +2734,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -3096,6 +3131,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -3180,6 +3216,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -3356,6 +3393,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -3676,6 +3714,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -3756,6 +3795,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -4375,6 +4415,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -4613,6 +4654,7 @@ impl FigureMgr {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
@ -4733,16 +4775,56 @@ impl FigureMgr {
|
||||
);
|
||||
},
|
||||
Body::Ship(body) => {
|
||||
let (model, skeleton_attr) = self.ship_model_cache.get_or_create_model(
|
||||
renderer,
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
&slow_jobs,
|
||||
);
|
||||
let (model, skeleton_attr) = if let Some(Collider::Volume(vol)) = collider {
|
||||
let vk = VolumeKey {
|
||||
entity,
|
||||
mut_count: vol.mut_count,
|
||||
};
|
||||
let (model, _skeleton_attr) = self.volume_model_cache.get_or_create_model(
|
||||
renderer,
|
||||
&mut self.col_lights,
|
||||
vk,
|
||||
inventory,
|
||||
vol.clone(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
&slow_jobs,
|
||||
);
|
||||
|
||||
let state = self
|
||||
.states
|
||||
.volume_states
|
||||
.entry(entity)
|
||||
.or_insert_with(|| FigureState::new(renderer, vk, vk));
|
||||
|
||||
state.update(
|
||||
renderer,
|
||||
&mut update_buf,
|
||||
&common_params,
|
||||
state_animation_rate,
|
||||
model,
|
||||
vk,
|
||||
);
|
||||
|
||||
break;
|
||||
} else if body.manifest_entry().is_some() {
|
||||
self.ship_model_cache.get_or_create_model(
|
||||
renderer,
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
&slow_jobs,
|
||||
)
|
||||
} else {
|
||||
println!("Cannot determine model");
|
||||
// No way to determine model
|
||||
break;
|
||||
};
|
||||
|
||||
let state = self.states.ship_states.entry(entity).or_insert_with(|| {
|
||||
FigureState::new(renderer, ShipSkeleton::default(), body)
|
||||
@ -4847,11 +4929,12 @@ impl FigureMgr {
|
||||
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))
|
||||
.for_each(|(entity, pos, _, body, _, inventory, scale)| {
|
||||
.filter(|(_, _, _, _, health, _, _, _)| health.map_or(true, |h| !h.is_dead))
|
||||
.for_each(|(entity, pos, _, body, _, inventory, scale, collider)| {
|
||||
if let Some((bound, model, _)) = self.get_model_for_render(
|
||||
tick,
|
||||
camera,
|
||||
@ -4862,6 +4945,10 @@ impl FigureMgr {
|
||||
false,
|
||||
pos.0,
|
||||
figure_lod_render_distance * scale.map_or(1.0, |s| s.0),
|
||||
match collider {
|
||||
Some(Collider::Volume(vol)) => vol.mut_count,
|
||||
_ => 0,
|
||||
},
|
||||
|state| state.can_shadow_sun(),
|
||||
) {
|
||||
drawer.draw(model, bound);
|
||||
@ -4884,19 +4971,20 @@ impl FigureMgr {
|
||||
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
|
||||
let character_state = character_state_storage.get(player_entity);
|
||||
|
||||
for (entity, pos, body, _, inventory, scale) in (
|
||||
for (entity, pos, body, _, inventory, scale, 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::<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))
|
||||
// Don't render player
|
||||
.filter(|(entity, _, _, _, _, _)| *entity != player_entity)
|
||||
.filter(|(entity, _, _, _, _, _, _)| *entity != player_entity)
|
||||
{
|
||||
if let Some((bound, model, col_lights)) = self.get_model_for_render(
|
||||
tick,
|
||||
@ -4908,6 +4996,10 @@ impl FigureMgr {
|
||||
false,
|
||||
pos.0,
|
||||
figure_lod_render_distance * scale.map_or(1.0, |s| s.0),
|
||||
match collider {
|
||||
Some(Collider::Volume(vol)) => vol.mut_count,
|
||||
_ => 0,
|
||||
},
|
||||
|state| state.visible(),
|
||||
) {
|
||||
drawer.draw(model, bound, col_lights);
|
||||
@ -4953,6 +5045,7 @@ impl FigureMgr {
|
||||
true,
|
||||
pos.0,
|
||||
figure_lod_render_distance,
|
||||
0,
|
||||
|state| state.visible(),
|
||||
) {
|
||||
drawer.draw(model, bound, col_lights);
|
||||
@ -4980,6 +5073,7 @@ impl FigureMgr {
|
||||
is_player: bool,
|
||||
pos: vek::Vec3<f32>,
|
||||
figure_lod_render_distance: f32,
|
||||
mut_count: usize,
|
||||
filter_state: impl Fn(&FigureStateMeta) -> bool,
|
||||
) -> Option<FigureModelRef> {
|
||||
let body = *body;
|
||||
@ -5010,6 +5104,7 @@ impl FigureMgr {
|
||||
object_model_cache,
|
||||
ship_model_cache,
|
||||
golem_model_cache,
|
||||
volume_model_cache,
|
||||
states:
|
||||
FigureMgrStates {
|
||||
character_states,
|
||||
@ -5027,6 +5122,7 @@ impl FigureMgr {
|
||||
golem_states,
|
||||
object_states,
|
||||
ship_states,
|
||||
volume_states,
|
||||
},
|
||||
} = self;
|
||||
let col_lights = &*col_lights_;
|
||||
@ -5255,22 +5351,43 @@ impl FigureMgr {
|
||||
),
|
||||
)
|
||||
}),
|
||||
Body::Ship(body) => ship_states
|
||||
.get(&entity)
|
||||
.filter(|state| filter_state(*state))
|
||||
.map(move |state| {
|
||||
(
|
||||
state.bound(),
|
||||
ship_model_cache.get_model(
|
||||
col_lights,
|
||||
body,
|
||||
inventory,
|
||||
tick,
|
||||
player_camera_mode,
|
||||
character_state,
|
||||
),
|
||||
)
|
||||
}),
|
||||
Body::Ship(body) => {
|
||||
if body.manifest_entry().is_some() {
|
||||
ship_states
|
||||
.get(&entity)
|
||||
.filter(|state| filter_state(*state))
|
||||
.map(move |state| {
|
||||
(
|
||||
state.bound(),
|
||||
ship_model_cache.get_model(
|
||||
col_lights,
|
||||
body,
|
||||
inventory,
|
||||
tick,
|
||||
player_camera_mode,
|
||||
character_state,
|
||||
),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
volume_states
|
||||
.get(&entity)
|
||||
.filter(|state| filter_state(*state))
|
||||
.map(move |state| {
|
||||
(
|
||||
state.bound(),
|
||||
volume_model_cache.get_model(
|
||||
col_lights,
|
||||
VolumeKey { entity, mut_count },
|
||||
inventory,
|
||||
tick,
|
||||
player_camera_mode,
|
||||
character_state,
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
} {
|
||||
let model_entry = model_entry?;
|
||||
|
||||
|
100
voxygen/src/scene/figure/volume.rs
Normal file
100
voxygen/src/scene/figure/volume.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use super::{
|
||||
cache::FigureKey,
|
||||
load::{BodySpec, BoneMeshes},
|
||||
EcsEntity,
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
comp::ship::figuredata::VoxelCollider,
|
||||
figure::{Cell, Segment},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use std::{convert::TryFrom, sync::Arc};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VolumeKey {
|
||||
pub entity: EcsEntity,
|
||||
pub mut_count: usize,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Self> for VolumeKey {
|
||||
fn from(this: &Self) -> Self { *this }
|
||||
}
|
||||
|
||||
impl anim::Skeleton for VolumeKey {
|
||||
type Attr = Self;
|
||||
type Body = Self;
|
||||
|
||||
const BONE_COUNT: usize = 4;
|
||||
|
||||
//#[cfg(feature = "use-dyn-lib")]
|
||||
// TODO
|
||||
|
||||
fn compute_matrices_inner(
|
||||
&self,
|
||||
base_mat: anim::vek::Mat4<f32>,
|
||||
buf: &mut [anim::FigureBoneData; anim::MAX_BONE_COUNT],
|
||||
_: Self::Body,
|
||||
) -> anim::Offsets {
|
||||
let scale_mat = anim::vek::Mat4::scaling_3d(1.0 / 11.0);
|
||||
|
||||
let bone = base_mat * scale_mat; // * anim::vek::Mat4::<f32>::identity();
|
||||
|
||||
*(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [
|
||||
anim::make_bone(bone),
|
||||
anim::make_bone(bone),
|
||||
anim::make_bone(bone),
|
||||
anim::make_bone(bone),
|
||||
];
|
||||
|
||||
anim::Offsets {
|
||||
lantern: None,
|
||||
mount_bone: anim::vek::Transform::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BodySpec for VolumeKey {
|
||||
type Extra = Arc<VoxelCollider>;
|
||||
type Manifests = ();
|
||||
type Spec = ();
|
||||
|
||||
fn load_spec() -> Result<Self::Manifests, assets::Error> { Ok(()) }
|
||||
|
||||
fn is_reloaded(_: &mut Self::Manifests) -> bool { false }
|
||||
|
||||
fn bone_meshes(
|
||||
_: &FigureKey<Self>,
|
||||
_: &Self::Manifests,
|
||||
collider: Self::Extra,
|
||||
) -> [Option<BoneMeshes>; anim::MAX_BONE_COUNT] {
|
||||
println!("Generating segment...");
|
||||
[
|
||||
Some((
|
||||
Segment::from_fn(collider.volume().sz, (), |pos| {
|
||||
match collider.volume().get(pos).unwrap().get_color() {
|
||||
Some(col) => Cell::new(col, false, false, false),
|
||||
None => Cell::Empty,
|
||||
}
|
||||
}),
|
||||
-collider.volume().sz.map(|e| e as f32) / 2.0,
|
||||
)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
]
|
||||
}
|
||||
}
|
@ -1198,7 +1198,9 @@ impl Scene {
|
||||
let hb_ori = [ori.x, ori.y, ori.z, ori.w];
|
||||
self.debug.set_context(*shape_id, hb_pos, color, hb_ori);
|
||||
},
|
||||
comp::Collider::Voxel { .. } | comp::Collider::Point => {
|
||||
comp::Collider::Voxel { .. }
|
||||
| comp::Collider::Volume(_)
|
||||
| comp::Collider::Point => {
|
||||
// ignore terrain-like or point-hitboxes
|
||||
},
|
||||
}
|
||||
|
@ -320,6 +320,7 @@ impl Scene {
|
||||
&mut self.col_lights,
|
||||
body,
|
||||
inventory,
|
||||
(),
|
||||
scene_data.tick,
|
||||
CameraMode::default(),
|
||||
None,
|
||||
|
Loading…
x
Reference in New Issue
Block a user