mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Vox spawn hack
This commit is contained in:
parent
61edae79ae
commit
6a6d68ddb9
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1421,9 +1421,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dot_vox"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83c18405ef54de0398b77a3ec8394d3a1639e7bf060e1385201e8db40c44ab41"
|
||||
version = "4.0.0"
|
||||
source = "git+https://github.com/Imberflur/dot_vox.git#23a877bcaeba0f86035a50bc49a89e5391eb38c7"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
@ -5744,6 +5743,7 @@ dependencies = [
|
||||
"dot_vox",
|
||||
"enum-iterator",
|
||||
"hashbrown 0.11.2",
|
||||
"image",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"num-derive",
|
||||
|
BIN
assets/coliseum.vox
(Stored with Git LFS)
Normal file
BIN
assets/coliseum.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
3
assets/place.ron
Normal file
3
assets/place.ron
Normal file
@ -0,0 +1,3 @@
|
||||
(pieces: [
|
||||
("coliseum", (0, 0, 0)),
|
||||
])
|
BIN
assets/thing.vox
(Stored with Git LFS)
Normal file
BIN
assets/thing.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -47,7 +47,8 @@ rand = "0.8"
|
||||
|
||||
# Assets
|
||||
common-assets = {package = "veloren-common-assets", path = "assets"}
|
||||
dot_vox = "4.0"
|
||||
dot_vox = { git = "https://github.com/Imberflur/dot_vox.git" }
|
||||
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||
|
||||
# Assets
|
||||
serde_repr = "0.1.6"
|
||||
|
@ -9,7 +9,7 @@ version = "0.10.0"
|
||||
lazy_static = "1.4.0"
|
||||
assets_manager = {version = "0.5.0", features = ["bincode", "ron", "json", "hot-reloading"]}
|
||||
ron = { version = "0.6", default-features = false }
|
||||
dot_vox = "4.0"
|
||||
dot_vox = { git = "https://github.com/Imberflur/dot_vox.git" }
|
||||
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||
tracing = "0.1"
|
||||
|
||||
|
@ -9,8 +9,8 @@ pub use self::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol},
|
||||
volumes::dyna::Dyna,
|
||||
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, VolSize, Vox, WriteVol},
|
||||
volumes::{chunk::Chunk, dyna::Dyna, vol_grid_3d::VolGrid3d},
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use vek::*;
|
||||
@ -252,3 +252,93 @@ impl MatSegment {
|
||||
impl From<&DotVoxData> for MatSegment {
|
||||
fn from(dot_vox_data: &DotVoxData) -> Self { Self::from_vox(dot_vox_data, false) }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SscSize;
|
||||
impl VolSize for SscSize {
|
||||
const SIZE: Vec3<u32> = Vec3 {
|
||||
x: 32,
|
||||
y: 32,
|
||||
z: 32,
|
||||
};
|
||||
}
|
||||
|
||||
pub type SparseScene = VolGrid3d<Chunk<Cell, SscSize, ()>>;
|
||||
|
||||
impl From<&DotVoxData> for SparseScene {
|
||||
fn from(dot_vox_data: &DotVoxData) -> Self {
|
||||
let mut sparse_scene = match VolGrid3d::new() {
|
||||
Ok(ok) => ok,
|
||||
Err(_) => panic!(),
|
||||
};
|
||||
|
||||
let mut models = dot_vox_data.scene.clone();
|
||||
|
||||
// Needed to work with vox files without a scene graph
|
||||
if models.is_empty() {
|
||||
models.push((
|
||||
dot_vox::Transform {
|
||||
t: [0, 0, 0],
|
||||
r: [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
|
||||
},
|
||||
0,
|
||||
));
|
||||
}
|
||||
|
||||
for (transform, model_id) in models {
|
||||
if let Some(model) = dot_vox_data.models.get(model_id) {
|
||||
let palette = dot_vox_data
|
||||
.palette
|
||||
.iter()
|
||||
.map(|col| Rgba::from(col.to_ne_bytes()).into())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Rotation
|
||||
let rot = Mat3::from_row_arrays(transform.r).map(|e| e as i32);
|
||||
// Get the rotated size of the model
|
||||
let size = rot.map(|e| e.abs() as u32)
|
||||
* Vec3::new(model.size.x, model.size.y, model.size.z);
|
||||
// Position of min corner
|
||||
let pos = Vec3::<i32>::from(transform.t)
|
||||
.map2(size, |m, s| (s, m))
|
||||
.map2(rot * Vec3::<i32>::one(), |(s, m), f| {
|
||||
m - (s as i32 + f.min(0) * -1) / 2
|
||||
});
|
||||
// dbg!(pos);
|
||||
// Insert required chunks
|
||||
let min_key = sparse_scene.pos_key(pos);
|
||||
let max_key = sparse_scene.pos_key(pos + size.map(|e| e as i32 - 1));
|
||||
for x in min_key.x..=max_key.x {
|
||||
for y in min_key.y..=max_key.y {
|
||||
for z in min_key.z..=max_key.z {
|
||||
let key = Vec3::new(x, y, z);
|
||||
if sparse_scene.get_key_arc(key).is_none() {
|
||||
sparse_scene.insert(
|
||||
key,
|
||||
std::sync::Arc::new(Chunk::filled(Cell::empty(), ())),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let offset = (rot
|
||||
* Vec3::new(model.size.x, model.size.y, model.size.z).map(|e| e as i32))
|
||||
.map(|e| if e > 0 { 0 } else { -e - 1 });
|
||||
for voxel in &model.voxels {
|
||||
if let Some(&color) = palette.get(voxel.i as usize) {
|
||||
sparse_scene
|
||||
.set(
|
||||
(rot * Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)))
|
||||
+ offset
|
||||
+ pos,
|
||||
Cell::new(color, false, false),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sparse_scene
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ crossbeam-utils = "0.8.1"
|
||||
crossbeam-channel = "0.5"
|
||||
# TODO: remove
|
||||
directories-next = "2.0"
|
||||
dot_vox = "4.0"
|
||||
dot_vox = { git = "https://github.com/Imberflur/dot_vox.git" }
|
||||
enum-iterator = "0.6"
|
||||
futures-executor = "0.3"
|
||||
guillotiere = "0.6"
|
||||
|
@ -174,6 +174,9 @@ impl From<&crate::settings::GamepadSettings> for ControllerSettings {
|
||||
map.entry(settings.game_buttons.swap_loadout)
|
||||
.or_default()
|
||||
.push(GameInput::SwapLoadout);
|
||||
map.entry(settings.game_buttons.place_vox)
|
||||
.or_default()
|
||||
.push(GameInput::PlaceVox);
|
||||
map
|
||||
},
|
||||
menu_button_map: {
|
||||
|
@ -9,9 +9,9 @@ use vek::*;
|
||||
|
||||
use client::{self, Client};
|
||||
use common::{
|
||||
assets::AssetExt,
|
||||
comp,
|
||||
assets::{self, Asset, AssetExt, AssetHandle, DotVoxAsset},
|
||||
comp::{
|
||||
self,
|
||||
inventory::slot::{EquipSlot, Slot},
|
||||
invite::InviteKind,
|
||||
item::{tool::ToolKind, ItemDef, ItemDesc},
|
||||
@ -32,6 +32,7 @@ use common_net::{
|
||||
msg::{server::InviteAnswer, PresenceKind},
|
||||
sync::WorldSyncExt,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
audio::sfx::SfxEvent,
|
||||
@ -54,6 +55,57 @@ enum TickAction {
|
||||
// Disconnected (i.e. go to main menu)
|
||||
Disconnect,
|
||||
}
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct VoxSpec(String, [i32; 3]);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct PlaceSpec {
|
||||
pieces: Vec<VoxSpec>,
|
||||
}
|
||||
|
||||
impl Asset for PlaceSpec {
|
||||
type Loader = assets::RonLoader;
|
||||
|
||||
const EXTENSION: &'static str = "ron";
|
||||
}
|
||||
|
||||
impl PlaceSpec {
|
||||
// pub fn load_watched() -> std::sync::Arc<Self> {
|
||||
// PlaceSpec::load("place")
|
||||
// }
|
||||
|
||||
pub fn build_place(&self) -> (common::figure::SparseScene, Vec3<i32>) {
|
||||
// TODO add sparse scene combination
|
||||
//use common::figure::{DynaUnionizer, Segment};
|
||||
fn graceful_load_vox(name: &str) -> AssetHandle<DotVoxAsset> {
|
||||
match DotVoxAsset::load(name) {
|
||||
Ok(dot_vox) => dot_vox,
|
||||
Err(_) => {
|
||||
error!("Could not load vox file for placement: {}", name);
|
||||
DotVoxAsset::load_expect("voxygen.voxel.not_found")
|
||||
},
|
||||
}
|
||||
}
|
||||
//let mut unionizer = DynaUnionizer::new();
|
||||
//for VoxSpec(specifier, offset) in &self.pieces {
|
||||
// let seg = Segment::from(graceful_load_vox(&specifier,
|
||||
// indicator).as_ref()); unionizer = unionizer.add(seg,
|
||||
// (*offset).into());
|
||||
//}
|
||||
|
||||
//unionizer.unify()
|
||||
let hack = "asset that doesn't exist";
|
||||
(
|
||||
match self.pieces.get(0) {
|
||||
Some(VoxSpec(specifier, _offset)) => {
|
||||
common::figure::SparseScene::from(&graceful_load_vox(&specifier).read().0)
|
||||
},
|
||||
None => common::figure::SparseScene::from(&graceful_load_vox(&hack).read().0),
|
||||
},
|
||||
Vec3::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SessionState {
|
||||
scene: Scene,
|
||||
@ -75,6 +127,14 @@ pub struct SessionState {
|
||||
interactable: Option<Interactable>,
|
||||
saved_zoom_dist: Option<f32>,
|
||||
hitboxes: HashMap<specs::Entity, DebugShapeId>,
|
||||
|
||||
placing_vox: Vec<(
|
||||
Vec3<i32>,
|
||||
Arc<common::volumes::chunk::Chunk<common::figure::Cell, common::figure::SscSize, ()>>,
|
||||
)>,
|
||||
last_sent: Option<(Vec3<i32>, Block)>,
|
||||
vox: common::figure::SparseScene,
|
||||
placepos: Vec3<i32>,
|
||||
}
|
||||
|
||||
/// Represents an active game session (i.e., the one being played).
|
||||
@ -123,6 +183,13 @@ impl SessionState {
|
||||
interactable: None,
|
||||
saved_zoom_dist: None,
|
||||
hitboxes: HashMap::new(),
|
||||
|
||||
placing_vox: Vec::new(),
|
||||
last_sent: None,
|
||||
// let mut place_indicator = PlaceSpec::load_expect("place");
|
||||
// TODO: use offset
|
||||
vox: PlaceSpec::load_expect("place").read().build_place().0,
|
||||
placepos: Vec3::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,6 +335,13 @@ impl SessionState {
|
||||
|
||||
impl PlayState for SessionState {
|
||||
fn enter(&mut self, global_state: &mut GlobalState, _: Direction) {
|
||||
// Clear vox spawining things
|
||||
self.placing_vox = Vec::new();
|
||||
self.last_sent = None;
|
||||
// TODO: use offset
|
||||
self.vox = PlaceSpec::load_expect("place").read().build_place().0;
|
||||
self.placepos = Vec3::zero();
|
||||
|
||||
// Trap the cursor.
|
||||
global_state.window.grab_cursor(true);
|
||||
|
||||
@ -290,6 +364,7 @@ impl PlayState for SessionState {
|
||||
let client = self.client.borrow();
|
||||
(client.presence(), client.registered())
|
||||
};
|
||||
|
||||
if client_presence.is_some() {
|
||||
let camera = self.scene.camera_mut();
|
||||
|
||||
@ -766,6 +841,24 @@ impl PlayState for SessionState {
|
||||
client.decline_invite();
|
||||
}
|
||||
},
|
||||
GameInput::PlaceVox if state => {
|
||||
// start placing
|
||||
if let Some(build_pos) = build_pos.filter(|_| can_build) {
|
||||
self.placepos = build_pos.map(|e| e.floor() as i32);
|
||||
// reload in case vox was changed
|
||||
self.vox =
|
||||
PlaceSpec::load_expect("place").read().build_place().0;
|
||||
self.placing_vox = self
|
||||
.vox
|
||||
.iter()
|
||||
.map(|(key, chunk)| (key, Arc::clone(chunk)))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.rev()
|
||||
.collect();
|
||||
self.last_sent = None;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -958,6 +1051,53 @@ impl PlayState for SessionState {
|
||||
.camera_mut()
|
||||
.compute_dependents(&*self.client.borrow().state().terrain());
|
||||
|
||||
// Only send next portion when the previous subchunk finishes sending
|
||||
if let Some((world_pos, block)) = self.last_sent.as_ref().copied() {
|
||||
if self
|
||||
.client
|
||||
.borrow()
|
||||
.state()
|
||||
.terrain()
|
||||
.get(world_pos)
|
||||
.map(|b| *b == block)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
self.last_sent = None;
|
||||
}
|
||||
}
|
||||
|
||||
if self.last_sent.is_none() {
|
||||
if let Some((key, chunk)) = self.placing_vox.pop() {
|
||||
use common::vol::IntoFullVolIterator;
|
||||
|
||||
for (pos, cell) in chunk.full_vol_iter() {
|
||||
let world_pos = self.vox.key_pos(key) + pos + self.placepos;
|
||||
match cell.get_color() {
|
||||
Some(color) => {
|
||||
let block = Block::new(BlockKind::Misc, color);
|
||||
self.client.borrow_mut().place_block(world_pos, block);
|
||||
self.last_sent = Some((world_pos, block))
|
||||
},
|
||||
None => {
|
||||
// Comment out this section to not carve out the empty space
|
||||
let mut client = self.client.borrow_mut();
|
||||
// Only remove the block if there is something there
|
||||
if client
|
||||
.state()
|
||||
.terrain()
|
||||
.get(world_pos)
|
||||
.map(|block| !block.is_fluid())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.remove_block(world_pos);
|
||||
self.last_sent = Some((world_pos, Block::empty()))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate debug info, if needed (it iterates through enough data that we might
|
||||
// as well avoid it unless we need it).
|
||||
let debug_info = global_state
|
||||
|
@ -170,6 +170,7 @@ impl ControlSettings {
|
||||
GameInput::DeclineGroupInvite => KeyMouse::Key(VirtualKeyCode::N),
|
||||
GameInput::MapZoomIn => KeyMouse::Key(VirtualKeyCode::Plus),
|
||||
GameInput::MapZoomOut => KeyMouse::Key(VirtualKeyCode::Minus),
|
||||
GameInput::PlaceVox => KeyMouse::Key(VirtualKeyCode::F7),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ pub mod con_settings {
|
||||
pub interact: Button,
|
||||
pub toggle_wield: Button,
|
||||
pub swap_loadout: Button,
|
||||
pub place_vox: Button,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@ -175,6 +176,7 @@ pub mod con_settings {
|
||||
interact: Button::Simple(GilButton::North),
|
||||
toggle_wield: Button::Simple(GilButton::West),
|
||||
swap_loadout: Button::Simple(GilButton::LeftThumb),
|
||||
place_vox: Button::Simple(GilButton::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ pub enum GameInput {
|
||||
DeclineGroupInvite,
|
||||
MapZoomIn,
|
||||
MapZoomOut,
|
||||
PlaceVox,
|
||||
}
|
||||
|
||||
impl GameInput {
|
||||
@ -145,6 +146,7 @@ impl GameInput {
|
||||
GameInput::DeclineGroupInvite => "gameinput.declinegroupinvite",
|
||||
GameInput::MapZoomIn => "gameinput.mapzoomin",
|
||||
GameInput::MapZoomOut => "gameinput.mapzoomout",
|
||||
GameInput::PlaceVox => "gameinput.placevox",
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +213,7 @@ impl GameInput {
|
||||
GameInput::DeclineGroupInvite,
|
||||
GameInput::MapZoomIn,
|
||||
GameInput::MapZoomOut,
|
||||
GameInput::PlaceVox,
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
|
Loading…
Reference in New Issue
Block a user