fix merge conflicts

Former-commit-id: 72614e5d57607a2095161857df45ce746e8c1b06
This commit is contained in:
Yeedo 2019-04-29 22:13:14 +01:00
commit 164feaa616
99 changed files with 1712 additions and 1635 deletions

8
.gitignore vendored
View File

@ -15,9 +15,7 @@
*.code-workspace
# Veloren
**/server_conf.toml
**/keybinds.toml
**/settings.toml
assets/voxygen
voxygen/keybinds.toml
settings.toml
*.rar
*.log

View File

@ -10,12 +10,14 @@ variables:
GIT_STRATEGY: none
before_script:
- mkdir -p /cache/veloren
- rm -rf /cache/veloren/bin
- cd /cache/veloren
- if [ -d .git ]; then
echo "is git dir";
else
git clone $CI_REPOSITORY_URL . ;
fi;
- mkdir -p /cache/veloren/bin
- rm -f .git/index.lock
- rm -f .git/shallow.lock
- rm -f .git/HEAD.lock
@ -28,7 +30,7 @@ before_script:
git pull "https://gitlab.com/${SOURCE_PROJECT}/veloren.git" "${SOURCE_BRANCH}";
fi;
- git submodule sync --recursive
- git submodule update --remote --recursive
- git submodule update --init --recursive --depth 20
- git status
- if [ -d target ]; then
ls -la target;
@ -42,7 +44,7 @@ build-voxygen:
stage: build
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- (cd voxygen && cargo build)
@ -50,7 +52,7 @@ build-server-cli:
stage: build
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- (cd server-cli && cargo build)
@ -62,7 +64,7 @@ unittests:
stage: test
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- cargo test
@ -70,7 +72,7 @@ benchmarktests:
stage: test
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- cargo bench
allow_failure: true
@ -84,25 +86,27 @@ clean-code:
stage: post-build
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- cargo fmt --all -- --check
allow_failure: true
coverage:
stage: post-build
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
script:
- cargo tarpaulin --skip-clean --all || echo "There is a problem in tarpaulin which sometimes fails"
allow_failure: true
# # Coverage needs to be disabled until an issue in the Rust compiler is fixed
# # https://github.com/rust-lang/rust/issues/58375
# coverage:
# stage: post-build
# image: registry.gitlab.com/veloren/veloren-docker-ci
# tags:
# - veloren-docker
# script:
# - cargo tarpaulin --all
# allow_failure: true
clippy:
stage: post-build
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- cargo clippy --all -- -D clippy || echo "This job is disabled, because we are not activly using it now, so we dont want to see yellow failed partly"
allow_failure: true
@ -115,7 +119,7 @@ commit-linux-debug:
stage: executable
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- (cd voxygen && VELOREN_ASSETS=assets cargo build)
- (cd server-cli && VELOREN_ASSETS=assets cargo build)
@ -125,9 +129,8 @@ commit-linux-debug:
- cp target/debug/veloren-voxygen commit-build
- cp -r assets commit-build/
- cp -r voxygen/shaders commit-build/
- rm -f commit-linux-debug.tar.bz2
- tar -cvjSf commit-linux-debug.tar.bz2 commit-build
- cp commit-linux-debug.tar.bz2 $CI_PROJECT_DIR
- tar -cvjSf bin/commit-linux-debug.tar.bz2 commit-build
- cp bin/commit-linux-debug.tar.bz2 $CI_PROJECT_DIR
artifacts:
paths:
- commit-linux-debug.tar.bz2
@ -137,13 +140,12 @@ commit-linux-debug:
only:
refs:
- master
- add-docker-gitlab-ci
commit-windows-debug:
stage: executable
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- (cd voxygen && VELOREN_ASSETS=assets cargo build --target=x86_64-pc-windows-gnu)
- (cd server-cli && VELOREN_ASSETS=assets cargo build --target=x86_64-pc-windows-gnu)
@ -153,9 +155,8 @@ commit-windows-debug:
- cp target/x86_64-pc-windows-gnu/debug/veloren-voxygen.exe commit-build
- cp -r assets commit-build/
- cp -r voxygen/shaders commit-build/
- rm -f commit-windows-debug.zip
- zip -r commit-windows-debug.zip commit-build
- cp commit-windows-debug.zip $CI_PROJECT_DIR
- zip -r bin/commit-windows-debug.zip commit-build
- cp bin/commit-windows-debug.zip $CI_PROJECT_DIR
artifacts:
paths:
- commit-windows-debug.zip
@ -165,7 +166,6 @@ commit-windows-debug:
only:
refs:
- master
- add-docker-gitlab-ci
#############
# NIGHTLY
@ -175,7 +175,7 @@ nightly-linux-optimized:
stage: executable
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- (cd voxygen && VELOREN_ASSETS=assets cargo build --release)
- (cd server-cli && VELOREN_ASSETS=assets cargo build --release)
@ -185,9 +185,8 @@ nightly-linux-optimized:
- cp target/release/veloren-voxygen nightly-build
- cp -r assets nightly-build/
- cp -r voxygen/shaders nightly-build/
- rm -f nightly-linux-optimized.tar.bz2
- tar -cvjSf nightly-linux-optimized.tar.bz2 nightly-build
- cp nightly-linux-optimized.zip $CI_PROJECT_DIR
- tar -cvjSf bin/nightly-linux-optimized.tar.bz2 nightly-build
- cp bin/nightly-linux-optimized.tar.bz2 $CI_PROJECT_DIR
artifacts:
paths:
- nightly-linux-optimized.tar.bz2
@ -199,7 +198,7 @@ nightly-windows-optimized:
stage: executable
image: registry.gitlab.com/veloren/veloren-docker-ci
tags:
- docker
- veloren-docker
script:
- (cd voxygen && VELOREN_ASSETS=assets cargo build --release --target=x86_64-pc-windows-gnu)
- (cd server-cli && VELOREN_ASSETS=assets cargo build --release --target=x86_64-pc-windows-gnu)
@ -209,9 +208,8 @@ nightly-windows-optimized:
- cp target/x86_64-pc-windows-gnu/release/veloren-voxygen.exe nightly-build
- cp -r assets nightly-build/
- cp -r voxygen/shaders nightly-build/
- rm -f nightly-windows-optimized.zip
- zip -r nightly-windows-optimized.zip nightly-build
- cp nightly-windows-optimized.zip $CI_PROJECT_DIR
- zip -r bin/nightly-windows-optimized.zip nightly-build
- cp bin/nightly-windows-optimized.zip $CI_PROJECT_DIR
artifacts:
paths:
- nightly-windows-optimized.zip

2
.gitmodules vendored
View File

@ -1,4 +1,4 @@
[submodule "assets/voxygen"]
path = assets/voxygen
url = https://gitlab.com/veloren/fresh-assets/voxygen.git
url = ../../veloren/fresh-assets/voxygen.git
shallow = true

View File

@ -2,6 +2,11 @@
<img alt="Veloren logo on a screenshot" src="https://cdn.discordapp.com/attachments/449602562165833760/521121348886593547/veloren_image.png">
</p>
[![pipeline status](https://gitlab.com/veloren/veloren/badges/master/pipeline.svg)](https://gitlab.com/veloren/veloren/commits/master)
[![coverage report](https://gitlab.com/veloren/veloren/badges/master/coverage.svg)](https://gitlab.com/veloren/veloren/commits/master)
[![license](https://img.shields.io/github/license/veloren/veloren.svg)](https://gitlab.com/veloren/veloren/blob/master/LICENSE)
[![discord](https://img.shields.io/discord/449602562165833758.svg)](https://discord.gg/WEXSY9h)
## Welcome To Veloren!
Veloren is a multiplayer voxel RPG written in Rust. Veloren takes inspiration from games such as Cube World, Minecraft and Dwarf Fortress. The game is currently under heavy development, but is playable.

@ -1 +1 @@
Subproject commit 25346c1ead7d03b11f7f4403332f2288824c6ca9
Subproject commit cff10b010db25ce9af5edbdfb5ef0af889dd741a

4
chat-cli/.gitignore vendored
View File

@ -1,4 +0,0 @@
/target
/assets
**/*.rs.bk.rar
Cargo.lock

View File

@ -1,10 +1,7 @@
use std::time::Duration;
use client::{Client, Event, Input};
use common::{clock::Clock, comp};
use log::info;
use client::{Input, Client, Event};
use common::{
comp,
clock::Clock,
};
use std::time::Duration;
const FPS: u64 = 60;
@ -18,8 +15,8 @@ fn main() {
let mut clock = Clock::new();
// Create client
let mut client = Client::new(([127, 0, 0, 1], 59003), 300)
.expect("Failed to create client instance");
let mut client =
Client::new(([127, 0, 0, 1], 59003), 300).expect("Failed to create client instance");
client.register(comp::Player::new("test".to_string()));
@ -31,7 +28,7 @@ fn main() {
Err(err) => {
println!("Error: {:?}", err);
break;
},
}
};
for event in events {

3
client/.gitignore vendored
View File

@ -1,3 +0,0 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -249,9 +249,18 @@ impl Client {
ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
ServerMsg::Pong => {}
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
ServerMsg::SetPlayerEntity(uid) => self.entity = self.state.ecs().entity_from_uid(uid).unwrap(), // TODO: Don't unwrap here!
ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package),
ServerMsg::EntityPhysics { entity, pos, vel, dir } => match self.state.ecs().entity_from_uid(entity) {
ServerMsg::SetPlayerEntity(uid) => {
self.entity = self.state.ecs().entity_from_uid(uid).unwrap()
} // TODO: Don't unwrap here!
ServerMsg::EcsSync(sync_package) => {
self.state.ecs_mut().sync_with_package(sync_package)
}
ServerMsg::EntityPhysics {
entity,
pos,
vel,
dir,
} => match self.state.ecs().entity_from_uid(entity) {
Some(entity) => {
self.state.write_component(entity, pos);
self.state.write_component(entity, vel);
@ -259,7 +268,10 @@ impl Client {
}
None => {}
},
ServerMsg::EntityAnimation { entity, animation_history } => match self.state.ecs().entity_from_uid(entity) {
ServerMsg::EntityAnimation {
entity,
animation_history,
} => match self.state.ecs().entity_from_uid(entity) {
Some(entity) => {
self.state.write_component(entity, animation_history);
}

3
common/.gitignore vendored
View File

@ -1,3 +0,0 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -1,8 +1,8 @@
use std::env;
use std::fs;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::fs::File;
fn try_load(name: &str) -> Option<File> {
let basepaths = [
@ -22,26 +22,29 @@ fn try_load(name: &str) -> Option<File> {
Ok(f) => {
debug!("loading {} succedeed", filename);
return Some(f);
},
}
Err(e) => {
debug!("loading {} did not work with error: {}", filename, e);
}
};
};
}
return None;
}
pub fn load(name: &str) -> Result<Vec<u8>,()> {
pub fn load(name: &str) -> Result<Vec<u8>, ()> {
return match try_load(name) {
Some(mut f) => {
let mut content: Vec<u8> = vec!();
let mut content: Vec<u8> = vec![];
f.read_to_end(&mut content);
info!("loaded asset successful: {}", name);
Ok(content)
},
}
None => {
warn!("Loading asset failed, wanted to load {} but could not load it, check debug log!", name);
warn!(
"Loading asset failed, wanted to load {} but could not load it, check debug log!",
name
);
Err(())
}
};
}
}

View File

@ -23,13 +23,19 @@ impl Clock {
}
#[allow(dead_code)]
pub fn get_tps(&self) -> f64 { 1.0 / self.running_tps_average }
pub fn get_tps(&self) -> f64 {
1.0 / self.running_tps_average
}
#[allow(dead_code)]
pub fn get_last_delta(&self) -> Duration { self.last_delta.unwrap_or(Duration::new(0, 0)) }
pub fn get_last_delta(&self) -> Duration {
self.last_delta.unwrap_or(Duration::new(0, 0))
}
#[allow(dead_code)]
pub fn get_avg_delta(&self) -> Duration { Duration::from_secs_f64(self.running_tps_average) }
pub fn get_avg_delta(&self) -> Duration {
Duration::from_secs_f64(self.running_tps_average)
}
#[allow(dead_code)]
pub fn tick(&mut self, tgt: Duration) {
@ -44,7 +50,9 @@ impl Clock {
} else {
tgt.as_secs_f64() / self.running_tps_average
};
thread::sleep(Duration::from_secs_f64(sleep_dur.as_secs_f64() * adjustment));
thread::sleep(Duration::from_secs_f64(
sleep_dur.as_secs_f64() * adjustment,
));
}
let delta = SystemTime::now()
@ -56,8 +64,8 @@ impl Clock {
self.running_tps_average = if self.running_tps_average == 0.0 {
delta.as_secs_f64()
} else {
CLOCK_SMOOTHING * self.running_tps_average +
(1.0 - CLOCK_SMOOTHING) * delta.as_secs_f64()
CLOCK_SMOOTHING * self.running_tps_average
+ (1.0 - CLOCK_SMOOTHING) * delta.as_secs_f64()
};
}
}

View File

@ -1,6 +1,6 @@
use specs::{Component, VecStorage, FlaggedStorage};
use vek::*;
use rand::prelude::*;
use specs::{Component, FlaggedStorage, VecStorage};
use vek::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Race {
@ -60,14 +60,14 @@ pub enum Weapon {
Staff,
}
use Race::*;
use Gender::*;
use Head::*;
use Chest::*;
use Belt::*;
use Pants::*;
use Hand::*;
use Chest::*;
use Foot::*;
use Gender::*;
use Hand::*;
use Head::*;
use Pants::*;
use Race::*;
use Weapon::*;
const ALL_RACES: [Race; 6] = [Danari, Dwarf, Elf, Human, Orc, Undead];

View File

@ -1,11 +1,11 @@
pub mod agent;
pub mod character;
pub mod player;
pub mod phys;
pub mod player;
// Reexports
pub use agent::{Agent, Control};
pub use character::Animation;
pub use character::AnimationHistory;
pub use character::Character;
pub use player::Player;
pub use character::AnimationHistory;
pub use character::Animation;

View File

@ -1,4 +1,4 @@
use specs::{Component, VecStorage, FlaggedStorage, NullStorage};
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
use vek::*;
// Pos

View File

@ -1,4 +1,4 @@
use specs::{Component, VecStorage, FlaggedStorage};
use specs::{Component, FlaggedStorage, VecStorage};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Player {
@ -7,9 +7,7 @@ pub struct Player {
impl Player {
pub fn new(alias: String) -> Self {
Self {
alias,
}
Self { alias }
}
}

View File

@ -1,8 +1,8 @@
pub mod cell;
// Library
use vek::*;
use dot_vox::DotVoxData;
use vek::*;
// Crate
use crate::{
@ -28,11 +28,7 @@ impl From<DotVoxData> for Segment {
.collect::<Vec<_>>();
let mut segment = Segment::filled(
Vec3::new(
model.size.x,
model.size.y,
model.size.z,
),
Vec3::new(model.size.x, model.size.y, model.size.z),
Cell::empty(),
(),
);

View File

@ -1,4 +1,9 @@
#![feature(euclidean_division, duration_float, trait_alias, bind_by_move_pattern_guards)]
#![feature(
euclidean_division,
duration_float,
trait_alias,
bind_by_move_pattern_guards
)]
#[macro_use]
extern crate serde_derive;
@ -10,13 +15,13 @@ pub mod clock;
pub mod comp;
pub mod figure;
pub mod msg;
pub mod ray;
pub mod state;
pub mod sys;
pub mod terrain;
pub mod util;
pub mod volumes;
pub mod vol;
pub mod ray;
pub mod volumes;
// TODO: unignore the code here, for some reason it refuses to compile here while has no problems copy-pasted elsewhere
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client

View File

@ -1,10 +1,12 @@
use vek::*;
use super::ClientState;
use crate::comp;
use vek::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientMsg {
Register { player: comp::Player },
Register {
player: comp::Player,
},
Character(comp::Character),
RequestState(ClientState),
Ping,

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData;
use serde_derive::{Serialize, Deserialize};
use crate::comp;
use serde_derive::{Deserialize, Serialize};
use std::marker::PhantomData;
// Automatically derive From<T> for Packet for each variant Packet::T(T)
sphynx::sum_type! {

View File

@ -1,11 +1,11 @@
pub mod client;
pub mod ecs_packet;
pub mod server;
pub mod client;
// Reexports
pub use self::server::{ServerMsg, RequestStateError};
pub use self::client::ClientMsg;
pub use self::ecs_packet::EcsPacket;
pub use self::server::{RequestStateError, ServerMsg};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum ClientState {

View File

@ -1,9 +1,6 @@
use super::{ClientState, EcsPacket};
use crate::{comp, terrain::TerrainChunk};
use vek::*;
use crate::{
comp,
terrain::TerrainChunk,
};
use super::{EcsPacket, ClientState};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RequestStateError {

View File

@ -7,11 +7,7 @@ pub use post2 as post;
// Reexports
pub use self::{
data::{ClientMsg, ServerMsg},
post::{
Error as PostError,
PostBox,
PostOffice,
},
post::{Error as PostError, PostBox, PostOffice},
};
pub trait PostSend = 'static + serde::Serialize + std::marker::Send + std::fmt::Debug;

View File

@ -1,14 +1,17 @@
use serde::{de::DeserializeOwned, Serialize};
use std::{
io::{self, Read, Write},
net::{TcpListener, TcpStream, SocketAddr, Shutdown},
time::{Instant, Duration},
marker::PhantomData,
sync::{mpsc, Arc, atomic::{AtomicBool, Ordering}},
thread,
collections::VecDeque,
convert::TryFrom,
io::{self, Read, Write},
marker::PhantomData,
net::{Shutdown, SocketAddr, TcpListener, TcpStream},
sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc,
},
thread,
time::{Duration, Instant},
};
use serde::{Serialize, de::DeserializeOwned};
#[derive(Clone, Debug)]
pub enum Error {
@ -62,7 +65,7 @@ impl<S: PostMsg, R: PostMsg> PostOffice<S, R> {
self.error.clone()
}
pub fn new_postboxes(&mut self) -> impl ExactSizeIterator<Item=PostBox<S, R>> {
pub fn new_postboxes(&mut self) -> impl ExactSizeIterator<Item = PostBox<S, R>> {
let mut new = Vec::new();
if self.error.is_some() {
@ -73,11 +76,11 @@ impl<S: PostMsg, R: PostMsg> PostOffice<S, R> {
match self.listener.accept() {
Ok((stream, sock)) => new.push(PostBox::from_stream(stream).unwrap()),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
Err(e) if e.kind() == io::ErrorKind::Interrupted => {},
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => {
self.error = Some(e.into());
break;
},
}
}
}
@ -136,11 +139,11 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
Err(e) => {
self.error = Some(e);
None
},
}
}
}
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item = R> {
let mut new = Vec::new();
if self.error.is_some() {
@ -154,18 +157,23 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
Err(e) => {
self.error = Some(e.into());
break;
},
}
Ok(Err(e)) => {
self.error = Some(e);
break;
},
}
}
}
new.into_iter()
}
fn worker(mut stream: TcpStream, send_rx: mpsc::Receiver<S>, recv_tx: mpsc::Sender<Result<R, Error>>, running: Arc<AtomicBool>) {
fn worker(
mut stream: TcpStream,
send_rx: mpsc::Receiver<S>,
recv_tx: mpsc::Sender<Result<R, Error>>,
running: Arc<AtomicBool>,
) {
let mut outgoing_chunks = VecDeque::new();
let mut incoming_buf = Vec::new();
@ -176,8 +184,8 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
Ok(Some(e)) | Err(e) => {
recv_tx.send(Err(e.into())).unwrap();
break 'work;
},
Ok(None) => {},
}
Ok(None) => {}
}
// Try getting messages from the send channel
@ -188,11 +196,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
let mut msg_bytes = bincode::serialize(&send_msg).unwrap();
// Assemble into packet
let mut packet_bytes = msg_bytes
.len()
.to_le_bytes()
.as_ref()
.to_vec();
let mut packet_bytes = msg_bytes.len().to_le_bytes().as_ref().to_vec();
packet_bytes.append(&mut msg_bytes);
// Split packet into chunks
@ -200,13 +204,13 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
.chunks(4096)
.map(|chunk| chunk.to_vec())
.for_each(|chunk| outgoing_chunks.push_back(chunk))
},
}
Err(mpsc::TryRecvError::Empty) => break,
// Worker error
Err(e) => {
let _ = recv_tx.send(Err(e.into()));
break 'work;
},
}
}
}
@ -218,17 +222,17 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
Ok(n) => {
outgoing_chunks.push_front(chunk.split_off(n));
break;
},
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
// Return chunk to the queue to try again later
outgoing_chunks.push_front(chunk);
break;
},
}
// Worker error
Err(e) => {
recv_tx.send(Err(e.into())).unwrap();
break 'work;
},
}
},
None => break,
}
@ -241,12 +245,12 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
match stream.read(&mut buf) {
Ok(n) => incoming_buf.extend_from_slice(&buf[0..n]),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
Err(e) if e.kind() == io::ErrorKind::Interrupted => {},
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
// Worker error
Err(e) => {
recv_tx.send(Err(e.into())).unwrap();
break 'work;
},
}
}
}
@ -262,16 +266,14 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
} else if incoming_buf.len() >= len + 8 {
match bincode::deserialize(&incoming_buf[8..len + 8]) {
Ok(msg) => recv_tx.send(Ok(msg)).unwrap(),
Err(err) => {
recv_tx.send(Err(err.into())).unwrap()
},
Err(err) => recv_tx.send(Err(err.into())).unwrap(),
}
incoming_buf = incoming_buf.split_off(len + 8);
} else {
break;
}
},
}
None => break,
}
}
@ -295,7 +297,9 @@ impl<S: PostMsg, R: PostMsg> Drop for PostBox<S, R> {
mod tests {
use super::*;
fn create_postoffice<S: PostMsg, R: PostMsg>(id: u16) -> Result<(PostOffice<S, R>, SocketAddr), Error> {
fn create_postoffice<S: PostMsg, R: PostMsg>(
id: u16,
) -> Result<(PostOffice<S, R>, SocketAddr), Error> {
let sock = ([0; 4], 12345 + id).into();
Ok((PostOffice::bind(sock)?, sock))
}
@ -353,9 +357,9 @@ mod tests {
}
let mut recv_msgs = Vec::new();
loop_for(Duration::from_millis(250), || server
.new_messages()
.for_each(|msg| recv_msgs.push(msg)));
loop_for(Duration::from_millis(250), || {
server.new_messages().for_each(|msg| recv_msgs.push(msg))
});
assert_eq!(test_msgs, recv_msgs);
}
@ -363,7 +367,9 @@ mod tests {
#[test]
fn send_recv_huge() {
let (mut postoffice, sock) = create_postoffice::<(), Vec<i32>>(3).unwrap();
let test_msgs: Vec<Vec<i32>> = (0..5).map(|i| (0..100000).map(|j| i * 2 + j).collect()).collect();
let test_msgs: Vec<Vec<i32>> = (0..5)
.map(|i| (0..100000).map(|j| i * 2 + j).collect())
.collect();
let mut client = PostBox::<Vec<i32>, ()>::to(sock).unwrap();
loop_for(Duration::from_millis(250), || ());
@ -374,9 +380,9 @@ mod tests {
}
let mut recv_msgs = Vec::new();
loop_for(Duration::from_millis(3000), || server
.new_messages()
.for_each(|msg| recv_msgs.push(msg)));
loop_for(Duration::from_millis(3000), || {
server.new_messages().for_each(|msg| recv_msgs.push(msg))
});
assert_eq!(test_msgs.len(), recv_msgs.len());
assert!(test_msgs == recv_msgs);

View File

@ -1,8 +1,5 @@
use crate::vol::{ReadVol, Vox};
use vek::*;
use crate::vol::{
Vox,
ReadVol,
};
pub trait RayUntil<V: Vox> = FnMut(&V) -> bool;
@ -26,10 +23,7 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
}
pub fn until(self, f: F) -> Ray<'a, V, F> {
Ray {
until: f,
..self
}
Ray { until: f, ..self }
}
pub fn max_iter(mut self, max_iter: usize) -> Self {
@ -52,19 +46,14 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
pos = self.from + dir * dist;
ipos = pos.map(|e| e as i32);
match self.vol
.get(ipos)
.map(|vox| (vox, (self.until)(vox)))
{
match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) {
Ok((vox, true)) => return (dist, Ok(Some(vox))),
Ok((_, false)) => {},
Ok((_, false)) => {}
Err(err) => return (dist, Err(err)),
}
let deltas = (
dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) -
pos.map(|e| e.fract())
) / dir;
let deltas =
(dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) - pos.map(|e| e.fract())) / dir;
dist += deltas.reduce(f32::min).max(PLANCK);
}

View File

@ -1,34 +1,21 @@
// Reexports
pub use sphynx::Uid;
use std::{
time::Duration,
collections::HashSet,
use crate::{
comp,
msg::EcsPacket,
sys,
terrain::{TerrainChunk, TerrainMap},
};
use shred::{Fetch, FetchMut};
use specs::{
Builder,
Component,
DispatcherBuilder,
EntityBuilder as EcsEntityBuilder,
Entity as EcsEntity,
storage::{
Storage as EcsStorage,
MaskedStorage as EcsMaskedStorage,
},
saveload::{MarkedBuilder, MarkerAllocator},
storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage},
Builder, Component, DispatcherBuilder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
};
use sphynx;
use std::{collections::HashSet, time::Duration};
use vek::*;
use crate::{
comp,
sys,
terrain::{
TerrainMap,
TerrainChunk,
},
msg::EcsPacket,
};
/// How much faster should an in-game day be compared to a real day?
// TODO: Don't hard-code this
@ -85,7 +72,11 @@ impl State {
/// Create a new `State` from an ECS state package
pub fn from_state_package(state_package: sphynx::StatePackage<EcsPacket>) -> Self {
Self {
ecs: sphynx::World::from_state_package(specs::World::new(), Self::setup_sphynx_world, state_package),
ecs: sphynx::World::from_state_package(
specs::World::new(),
Self::setup_sphynx_world,
state_package,
),
changes: Changes::default(),
}
}
@ -113,7 +104,8 @@ impl State {
/// Register a component with the state's ECS
pub fn with_component<T: Component>(mut self) -> Self
where <T as Component>::Storage: Default
where
<T as Component>::Storage: Default,
{
self.ecs.register::<T>();
self
@ -166,13 +158,13 @@ impl State {
/// Get a reference to this state's terrain.
pub fn terrain(&self) -> Fetch<TerrainMap> {
self.ecs
.read_resource::<TerrainMap>()
self.ecs.read_resource::<TerrainMap>()
}
/// Insert the provided chunk into this state's terrain.
pub fn insert_chunk(&mut self, key: Vec3<i32>, chunk: TerrainChunk) {
if self.ecs
if self
.ecs
.write_resource::<TerrainMap>()
.insert(key, chunk)
.is_some()
@ -185,7 +177,8 @@ impl State {
/// Remove the chunk with the given key from this state's terrain, if it exists
pub fn remove_chunk(&mut self, key: Vec3<i32>) {
if self.ecs
if self
.ecs
.write_resource::<TerrainMap>()
.remove(key)
.is_some()

View File

@ -3,7 +3,7 @@ use specs::{Join, Read, ReadStorage, System, WriteStorage};
use vek::*;
// Crate
use crate::comp::{Agent, Control, phys::Pos};
use crate::comp::{phys::Pos, Agent, Control};
// Basic ECS AI agent system
pub struct Sys;
@ -22,12 +22,14 @@ impl<'a> System<'a> for Sys {
*bearing += Vec2::new(
rand::random::<f32>().fract() - 0.5,
rand::random::<f32>().fract() - 0.5,
) * 0.1 - *bearing * 0.01 - pos.0 * 0.0002;
) * 0.1
- *bearing * 0.01
- pos.0 * 0.0002;
if bearing.magnitude_squared() != 0.0 {
control.move_dir = bearing.normalized();
}
},
}
}
}
}

View File

@ -1,9 +1,12 @@
// Library
use specs::{Join, Read, ReadStorage, System, WriteStorage, Entities};
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
use vek::*;
// Crate
use crate::comp::{Control, Animation, AnimationHistory, phys::{Pos, Vel, Dir}};
use crate::comp::{
phys::{Dir, Pos, Vel},
Animation, AnimationHistory, Control,
};
// Basic ECS AI agent system
pub struct Sys;
@ -18,25 +21,29 @@ impl<'a> System<'a> for Sys {
);
fn run(&mut self, (entities, mut vels, mut dirs, mut anims, controls): Self::SystemData) {
for (entity, mut vel, mut dir, control) in (&entities, &mut vels, &mut dirs, &controls).join() {
for (entity, mut vel, mut dir, control) in
(&entities, &mut vels, &mut dirs, &controls).join()
{
// TODO: Don't hard-code this
// Apply physics to the player: acceleration and non-linear decceleration
vel.0 += control.move_dir * 2.0 - vel.0.map(|e| e * e.abs() + e) * 0.03;
let animation =
if control.move_dir.magnitude() > 0.01 {
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
Animation::Run
} else {
Animation::Idle
};
let animation = if control.move_dir.magnitude() > 0.01 {
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
Animation::Run
} else {
Animation::Idle
};
let last_animation = anims.get_mut(entity).map(|h| h.current);
anims.insert(entity, AnimationHistory {
last: last_animation,
current: animation,
});
anims.insert(
entity,
AnimationHistory {
last: last_animation,
current: animation,
},
);
}
}
}

View File

@ -1,11 +1,11 @@
use vek::*;
use specs::{Join, Read, ReadStorage, System, WriteStorage, ReadExpect};
use crate::{
comp::phys::{Pos, Vel},
state::DeltaTime,
terrain::TerrainMap,
vol::{Vox, ReadVol},
vol::{ReadVol, Vox},
};
use specs::{Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use vek::*;
// Basic ECS physics system
pub struct Sys;
@ -33,8 +33,8 @@ impl<'a> System<'a> for Sys {
while terrain
.get(pos.0.map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false) &&
i < 80
.unwrap_or(false)
&& i < 80
{
pos.0.z += 0.005;
vel.0.z = 0.0;

View File

@ -1,4 +1,4 @@
use serde_derive::{Serialize, Deserialize};
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BiomeKind {

View File

@ -1,5 +1,5 @@
use serde_derive::{Deserialize, Serialize};
use vek::*;
use serde_derive::{Serialize, Deserialize};
// Crate
use crate::vol::Vox;

View File

@ -1,21 +1,15 @@
pub mod block;
pub mod biome;
pub mod block;
// Reexports
pub use self::{
block::Block,
biome::BiomeKind,
};
pub use self::{biome::BiomeKind, block::Block};
use vek::*;
use serde_derive::{Serialize, Deserialize};
use crate::{
vol::VolSize,
volumes::{
vol_map::VolMap,
chunk::Chunk,
},
volumes::{chunk::Chunk, vol_map::VolMap},
};
use serde_derive::{Deserialize, Serialize};
use vek::*;
// TerrainChunkSize
@ -23,7 +17,11 @@ use crate::{
pub struct TerrainChunkSize;
impl VolSize for TerrainChunkSize {
const SIZE: Vec3<u32> = Vec3 { x: 32, y: 32, z: 32 };
const SIZE: Vec3<u32> = Vec3 {
x: 32,
y: 32,
z: 32,
};
}
// TerrainChunkMeta

View File

@ -1,5 +1,5 @@
use vek::*;
use crate::ray::{Ray, RayUntil};
use vek::*;
/// A voxel
pub trait Vox {
@ -66,14 +66,18 @@ pub trait ReadVol: BaseVol {
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>;
fn ray(&self, from: Vec3<f32>, to: Vec3<f32>) -> Ray<Self, fn(&Self::Vox) -> bool>
where Self: Sized
where
Self: Sized,
{
Ray::new(self, from, to, |vox| !vox.is_empty())
}
}
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.
pub trait SampleVol: BaseVol where Self::Vox: Clone {
pub trait SampleVol: BaseVol
where
Self::Vox: Clone,
{
type Sample: BaseVol + ReadVol;
/// Take a sample of the volume by cloning voxels within the provided range.
///

View File

@ -2,18 +2,11 @@
use std::marker::PhantomData;
// Library
use serde_derive::{Deserialize, Serialize};
use vek::*;
use serde_derive::{Serialize, Deserialize};
// Local
use crate::vol::{
Vox,
BaseVol,
SizedVol,
ReadVol,
WriteVol,
VolSize,
};
use crate::vol::{BaseVol, ReadVol, SizedVol, VolSize, Vox, WriteVol};
#[derive(Debug)]
pub enum ChunkErr {
@ -36,15 +29,13 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
// array.
#[inline(always)]
fn idx_for(pos: Vec3<i32>) -> Option<usize> {
if
pos.map(|e| e >= 0).reduce_and() &&
pos.map2(S::SIZE, |e, lim| e < lim as i32).reduce_and()
if pos.map(|e| e >= 0).reduce_and()
&& pos.map2(S::SIZE, |e, lim| e < lim as i32).reduce_and()
{
Some((
pos.x * S::SIZE.y as i32 * S::SIZE.z as i32 +
pos.y * S::SIZE.z as i32 +
pos.z
) as usize)
Some(
(pos.x * S::SIZE.y as i32 * S::SIZE.z as i32 + pos.y * S::SIZE.z as i32 + pos.z)
as usize,
)
} else {
None
}
@ -58,7 +49,9 @@ impl<V: Vox, S: VolSize, M> BaseVol for Chunk<V, S, M> {
impl<V: Vox, S: VolSize, M> SizedVol for Chunk<V, S, M> {
#[inline(always)]
fn get_size(&self) -> Vec3<u32> { S::SIZE }
fn get_size(&self) -> Vec3<u32> {
S::SIZE
}
}
impl<V: Vox, S: VolSize, M> ReadVol for Chunk<V, S, M> {

View File

@ -2,13 +2,7 @@
use vek::*;
// Local
use crate::vol::{
Vox,
BaseVol,
SizedVol,
ReadVol,
WriteVol,
};
use crate::vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol};
#[derive(Debug)]
pub enum DynaErr {
@ -30,15 +24,8 @@ impl<V: Vox, M> Dyna<V, M> {
// array.
#[inline(always)]
fn idx_for(sz: Vec3<u32>, pos: Vec3<i32>) -> Option<usize> {
if
pos.map(|e| e >= 0).reduce_and() &&
pos.map2(sz, |e, lim| e < lim as i32).reduce_and()
{
Some((
pos.x * sz.y as i32 * sz.z as i32 +
pos.y * sz.z as i32 +
pos.z
) as usize)
if pos.map(|e| e >= 0).reduce_and() && pos.map2(sz, |e, lim| e < lim as i32).reduce_and() {
Some((pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize)
} else {
None
}
@ -52,7 +39,9 @@ impl<V: Vox, M> BaseVol for Dyna<V, M> {
impl<V: Vox, M> SizedVol for Dyna<V, M> {
#[inline(always)]
fn get_size(&self) -> Vec3<u32> { self.sz }
fn get_size(&self) -> Vec3<u32> {
self.sz
}
}
impl<V: Vox, M> ReadVol for Dyna<V, M> {

View File

@ -1,3 +1,3 @@
pub mod dyna;
pub mod chunk;
pub mod dyna;
pub mod vol_map;

View File

@ -6,15 +6,7 @@ use vek::*;
// Crate
use crate::{
vol::{
Vox,
BaseVol,
SizedVol,
ReadVol,
SampleVol,
WriteVol,
VolSize,
},
vol::{BaseVol, ReadVol, SampleVol, SizedVol, VolSize, Vox, WriteVol},
volumes::{
chunk::{Chunk, ChunkErr},
dyna::{Dyna, DynaErr},
@ -56,7 +48,8 @@ impl<V: Vox, S: VolSize, M> ReadVol for VolMap<V, S, M> {
#[inline(always)]
fn get(&self, pos: Vec3<i32>) -> Result<&V, VolMapErr> {
let ck = Self::chunk_key(pos);
self.chunks.get(&ck)
self.chunks
.get(&ck)
.ok_or(VolMapErr::NoSuchChunk)
.and_then(|chunk| {
let co = Self::chunk_offs(pos);
@ -87,14 +80,16 @@ impl<V: Vox + Clone, S: VolSize, M> SampleVol for VolMap<V, S, M> {
}
*/
let mut sample = Dyna::filled(
range.size().map(|e| e as u32).into(),
V::empty(),
(),
);
let mut sample = Dyna::filled(range.size().map(|e| e as u32).into(), V::empty(), ());
for pos in sample.iter_positions() {
sample.set(pos, self.get(range.min + pos).map(|v| v.clone()).unwrap_or(V::empty()))
sample
.set(
pos,
self.get(range.min + pos)
.map(|v| v.clone())
.unwrap_or(V::empty()),
)
.map_err(|err| VolMapErr::DynaErr(err))?;
}
@ -106,7 +101,8 @@ impl<V: Vox, S: VolSize, M> WriteVol for VolMap<V, S, M> {
#[inline(always)]
fn set(&mut self, pos: Vec3<i32>, vox: V) -> Result<(), VolMapErr> {
let ck = Self::chunk_key(pos);
self.chunks.get_mut(&ck)
self.chunks
.get_mut(&ck)
.ok_or(VolMapErr::NoSuchChunk)
.and_then(|chunk| {
let co = Self::chunk_offs(pos);
@ -122,7 +118,9 @@ impl<V: Vox, S: VolSize, M> VolMap<V, S, M> {
}
}
pub fn chunk_size() -> Vec3<u32> { S::SIZE }
pub fn chunk_size() -> Vec3<u32> {
S::SIZE
}
pub fn insert(&mut self, key: Vec3<i32>, chunk: Chunk<V, S, M>) -> Option<Chunk<V, S, M>> {
self.chunks.insert(key, chunk)

View File

@ -1,3 +0,0 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -1,7 +1,7 @@
use std::time::Duration;
use log::info;
use server::{Input, Event, Server};
use common::clock::Clock;
use log::info;
use server::{Event, Input, Server};
use std::time::Duration;
const TPS: u64 = 30;
@ -15,11 +15,11 @@ fn main() {
let mut clock = Clock::new();
// Create server
let mut server = Server::new()
.expect("Failed to create server instance");
let mut server = Server::new().expect("Failed to create server instance");
loop {
let events = server.tick(Input::default(), clock.get_last_delta())
let events = server
.tick(Input::default(), clock.get_last_delta())
.expect("Failed to tick server");
for event in events {

3
server/.gitignore vendored
View File

@ -1,3 +0,0 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -88,9 +88,14 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
.read_component_cloned::<comp::phys::Pos>(entity)
{
Some(current_pos) => {
server.state.write_component(entity, comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z)));
server.state.write_component(entity, comp::phys::ForceUpdate);
},
server.state.write_component(
entity,
comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z)),
);
server
.state
.write_component(entity, comp::phys::ForceUpdate);
}
None => server.clients.notify(
entity,
ServerMsg::Chat(String::from("Command 'jump' invalid in current state")),
@ -107,9 +112,13 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32);
match (opt_x, opt_y, opt_z) {
(Some(x), Some(y), Some(z)) => {
server.state.write_component(entity, comp::phys::Pos(Vec3::new(x, y, z)));
server.state.write_component(entity, comp::phys::ForceUpdate);
},
server
.state
.write_component(entity, comp::phys::Pos(Vec3::new(x, y, z)));
server
.state
.write_component(entity, comp::phys::ForceUpdate);
}
_ => server
.clients
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
@ -144,8 +153,10 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
{
Some(pos) => {
server.state.write_component(entity, pos);
server.state.write_component(entity, comp::phys::ForceUpdate);
},
server
.state
.write_component(entity, comp::phys::ForceUpdate);
}
None => server.clients.notify(
entity,
ServerMsg::Chat(format!("Unable to teleport to player '{}'", alias)),

View File

@ -8,20 +8,10 @@ pub mod input;
// Reexports
pub use crate::{error::Error, input::Input};
use std::{
collections::HashSet,
net::SocketAddr,
sync::mpsc,
time::Duration,
i32,
use crate::{
client::{Client, Clients},
cmd::CHAT_COMMANDS,
};
use specs::{
join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder,
Entity as EcsEntity,
};
use threadpool::ThreadPool;
use vek::*;
use world::World;
use common::{
comp,
comp::character::Animation,
@ -30,10 +20,14 @@ use common::{
state::{State, Uid},
terrain::TerrainChunk,
};
use crate::{
client::{Client, Clients},
cmd::CHAT_COMMANDS,
use specs::{
join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder,
Entity as EcsEntity,
};
use std::{collections::HashSet, i32, net::SocketAddr, sync::mpsc, time::Duration};
use threadpool::ThreadPool;
use vek::*;
use world::World;
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
@ -193,15 +187,20 @@ impl Server {
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Player>(),
&self.state.ecs().read_storage::<comp::phys::Pos>(),
).join() {
)
.join()
{
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
let dist = (chunk_pos - key).map(|e| e.abs()).reduce_max();
if dist < 4 {
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
key,
chunk: Box::new(chunk.clone()),
});
self.clients.notify(
entity,
ServerMsg::TerrainChunkUpdate {
key,
chunk: Box::new(chunk.clone()),
},
);
}
}
@ -218,7 +217,9 @@ impl Server {
for (_, pos) in (
&self.state.ecs().read_storage::<comp::Player>(),
&self.state.ecs().read_storage::<comp::phys::Pos>(),
).join() {
)
.join()
{
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
let dist = (chunk_pos - key).map(|e| e.abs()).reduce_max();
min_dist = min_dist.min(dist);
@ -297,43 +298,68 @@ impl Server {
ClientState::Connected => disconnect = true, // Default state
ClientState::Registered => match client.client_state {
// Use ClientMsg::Register instead
ClientState::Connected => client.error_state(RequestStateError::WrongMessage),
ClientState::Registered => client.error_state(RequestStateError::Already),
ClientState::Spectator | ClientState::Character
=> client.allow_state(ClientState::Registered),
ClientState::Connected => {
client.error_state(RequestStateError::WrongMessage)
}
ClientState::Registered => {
client.error_state(RequestStateError::Already)
}
ClientState::Spectator | ClientState::Character => {
client.allow_state(ClientState::Registered)
}
},
ClientState::Spectator => match requested_state {
// Become Registered first
ClientState::Connected => client.error_state(RequestStateError::Impossible),
ClientState::Spectator => client.error_state(RequestStateError::Already),
ClientState::Registered | ClientState::Character
=> client.allow_state(ClientState::Spectator),
ClientState::Connected => {
client.error_state(RequestStateError::Impossible)
}
ClientState::Spectator => {
client.error_state(RequestStateError::Already)
}
ClientState::Registered | ClientState::Character => {
client.allow_state(ClientState::Spectator)
}
},
// Use ClientMsg::Character instead
ClientState::Character => client.error_state(RequestStateError::WrongMessage),
ClientState::Character => {
client.error_state(RequestStateError::WrongMessage)
}
},
ClientMsg::Register { player } => match client.client_state {
ClientState::Connected => Self::initialize_player(state, entity, client, player),
ClientState::Connected => {
Self::initialize_player(state, entity, client, player)
}
// Use RequestState instead (No need to send `player` again)
_ => client.error_state(RequestStateError::Impossible),
},
ClientMsg::Character(character) => match client.client_state {
// Become Registered first
ClientState::Connected => client.error_state(RequestStateError::Impossible),
ClientState::Registered | ClientState::Spectator =>
Self::create_player_character(state, entity, client, character),
ClientState::Character => client.error_state(RequestStateError::Already),
ClientState::Connected => {
client.error_state(RequestStateError::Impossible)
}
ClientState::Registered | ClientState::Spectator => {
Self::create_player_character(state, entity, client, character)
}
ClientState::Character => {
client.error_state(RequestStateError::Already)
}
},
ClientMsg::Chat(msg) => match client.client_state {
ClientState::Connected => client.error_state(RequestStateError::Impossible),
ClientState::Connected => {
client.error_state(RequestStateError::Impossible)
}
ClientState::Registered
| ClientState::Spectator
| ClientState::Character => new_chat_msgs.push((entity, msg)),
},
ClientMsg::PlayerAnimation(animation_history) => match client.client_state {
ClientState::Character => state.write_component(entity, animation_history),
// Only characters can send animations
_ => client.error_state(RequestStateError::Impossible),
ClientMsg::PlayerAnimation(animation_history) => {
match client.client_state {
ClientState::Character => {
state.write_component(entity, animation_history)
}
// Only characters can send animations
_ => client.error_state(RequestStateError::Impossible),
}
}
ClientMsg::PlayerPhysics { pos, vel, dir } => match client.client_state {
ClientState::Character => {
@ -350,10 +376,12 @@ impl Server {
}
ClientState::Spectator | ClientState::Character => {
match state.terrain().get_key(key) {
Some(chunk) => client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
key,
chunk: Box::new(chunk.clone()),
}),
Some(chunk) => {
client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
key,
chunk: Box::new(chunk.clone()),
})
}
None => requested_chunks.push(key),
}
}
@ -458,7 +486,10 @@ impl Server {
&self.state.ecs().read_storage::<comp::phys::Pos>(),
&self.state.ecs().read_storage::<comp::phys::Vel>(),
&self.state.ecs().read_storage::<comp::phys::Dir>(),
self.state.ecs().read_storage::<comp::phys::ForceUpdate>().maybe(),
self.state
.ecs()
.read_storage::<comp::phys::ForceUpdate>()
.maybe(),
)
.join()
{
@ -508,7 +539,10 @@ impl Server {
}
// Remove all force flags
self.state.ecs_mut().write_storage::<comp::phys::ForceUpdate>().clear();
self.state
.ecs_mut()
.write_storage::<comp::phys::ForceUpdate>()
.clear();
}
pub fn generate_chunk(&mut self, key: Vec3<i32>) {

5
voxygen/.gitignore vendored
View File

@ -1,5 +0,0 @@
/target
**/*.rs.bk
Cargo.lock
settings.toml
voxygen.log

View File

@ -5,11 +5,7 @@ use std::f32::consts::PI;
use vek::*;
// Local
use super::{
CharacterSkeleton,
super::Animation,
SCALE,
};
use super::{super::Animation, CharacterSkeleton, SCALE};
pub struct IdleAnimation;
@ -17,10 +13,7 @@ impl Animation for IdleAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = f64;
fn update_skeleton(
skeleton: &Self::Skeleton,
time: f64,
) -> Self::Skeleton {
fn update_skeleton(skeleton: &Self::Skeleton, time: f64) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let wave = (time as f32 * 12.0).sin();
@ -47,11 +40,19 @@ impl Animation for IdleAnimation {
next.shorts.ori = Quaternion::rotation_y(0.0);
next.shorts.scale = Vec3::one();
next.l_hand.offset = Vec3::new(2.0 + waveultracos_slow * 0.3, 7.5, 12.5 + waveultra_slow * 1.1);
next.l_hand.offset = Vec3::new(
2.0 + waveultracos_slow * 0.3,
7.5,
12.5 + waveultra_slow * 1.1,
);
next.l_hand.ori = Quaternion::rotation_y(0.0 + waveultra_slow * 0.06);
next.l_hand.scale = Vec3::one();
next.r_hand.offset = Vec3::new(2.0 + waveultracos_slow * 0.3 , - 7.5, 12.5 + waveultra_slow * 1.1);
next.r_hand.offset = Vec3::new(
2.0 + waveultracos_slow * 0.3,
-7.5,
12.5 + waveultra_slow * 1.1,
);
next.r_hand.ori = Quaternion::rotation_y(0.0 + waveultra_slow * 0.06);
next.r_hand.scale = Vec3::one();

View File

@ -1,18 +1,15 @@
pub mod run;
pub mod idle;
pub mod run;
// Reexports
pub use self::run::RunAnimation;
pub use self::idle::IdleAnimation;
pub use self::run::RunAnimation;
// Crate
use crate::render::FigureBoneData;
// Local
use super::{
Skeleton,
Bone,
};
use super::{Bone, Skeleton};
const SCALE: f32 = 11.0;
@ -47,7 +44,6 @@ impl CharacterSkeleton {
torso: Bone::default(),
l_shoulder: Bone::default(),
r_shoulder: Bone::default(),
}
}
}
@ -75,7 +71,6 @@ impl Skeleton for CharacterSkeleton {
FigureBoneData::default(),
FigureBoneData::default(),
FigureBoneData::default(),
]
}
@ -92,6 +87,5 @@ impl Skeleton for CharacterSkeleton {
self.torso.interpolate(&target.torso);
self.l_shoulder.interpolate(&target.l_shoulder);
self.r_shoulder.interpolate(&target.r_shoulder);
}
}

View File

@ -5,11 +5,7 @@ use std::f32::consts::PI;
use vek::*;
// Local
use super::{
CharacterSkeleton,
super::Animation,
SCALE
};
use super::{super::Animation, CharacterSkeleton, SCALE};
pub struct RunAnimation;
@ -17,10 +13,7 @@ impl Animation for RunAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = f64;
fn update_skeleton(
skeleton: &Self::Skeleton,
time: f64,
) -> Self::Skeleton {
fn update_skeleton(skeleton: &Self::Skeleton, time: f64) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let wave = (time as f32 * 14.0).sin();
@ -79,8 +72,6 @@ impl Animation for RunAnimation {
next.r_shoulder.ori = Quaternion::rotation_y(0.0);
next.r_shoulder.scale = Vec3::one();
next
}
}

View File

@ -23,7 +23,9 @@ impl Bone {
}
pub fn compute_base_matrix(&self) -> Mat4<f32> {
Mat4::<f32>::translation_3d(self.offset) * Mat4::scaling_3d(self.scale) * Mat4::from(self.ori)
Mat4::<f32>::translation_3d(self.offset)
* Mat4::scaling_3d(self.scale)
* Mat4::from(self.ori)
}
/// Change the current bone to be more like `target`
@ -48,8 +50,5 @@ pub trait Animation {
type Dependency;
/// Returns a new skeleton that is generated by the animation
fn update_skeleton(
skeleton: &Self::Skeleton,
dependency: Self::Dependency,
) -> Self::Skeleton;
fn update_skeleton(skeleton: &Self::Skeleton, dependency: Self::Dependency) -> Self::Skeleton;
}

View File

@ -85,7 +85,11 @@ impl Chat {
// Only show if it has the keyboard captured
// Chat input with rectangle as background
let keyboard_captured = ui_widgets.global_input().current.widget_capturing_keyboard.map_or(false, |id| id == self.ids.input);
let keyboard_captured = ui_widgets
.global_input()
.current
.widget_capturing_keyboard
.map_or(false, |id| id == self.ids.input);
if keyboard_captured {
let text_edit = TextEdit::new(&self.input)
.w(460.0)
@ -114,10 +118,12 @@ impl Chat {
// Message box
Rectangle::fill([470.0, 174.0])
.rgba(0.0, 0.0, 0.0, 0.4)
.and(|r| if keyboard_captured {
r.up_from(self.ids.input_bg, 0.0)
} else {
r.bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0)
.and(|r| {
if keyboard_captured {
r.up_from(self.ids.input_bg, 0.0)
} else {
r.bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0)
}
})
.set(self.ids.message_box_bg, ui_widgets);
let (mut items, _) = List::flow_down(self.messages.len() + 1)

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
#![feature(drain_filter)]
#![recursion_limit="2048"]
#![recursion_limit = "2048"]
pub mod anim;
pub mod error;
@ -10,22 +10,18 @@ pub mod mesh;
pub mod render;
pub mod scene;
pub mod session;
pub mod ui;
pub mod window;
pub mod settings;
pub mod singleplayer;
pub mod ui;
pub mod window;
// Reexports
pub use crate::error::Error;
use std::{mem, thread, panic, fs::File};
use crate::{menu::main::MainMenuState, settings::Settings, window::Window};
use log;
use simplelog::{CombinedLogger, TermLogger, WriteLogger, Config};
use crate::{
menu::main::MainMenuState,
window::Window,
settings::Settings,
};
use simplelog::{CombinedLogger, Config, TermLogger, WriteLogger};
use std::{fs::File, mem, panic, str::FromStr, thread};
/// The URL of the default public server that Voxygen will connect to
const DEFAULT_PUBLIC_SERVER: &'static str = "server.veloren.net";
@ -82,15 +78,24 @@ fn main() {
let settings = Settings::default();
settings.save_to_file();
settings
},
}
};
let window = Window::new(&settings).expect("Failed to create window");
// Init logging
let term_log_level = std::env::var_os("VOXYGEN_LOG")
.and_then(|env| env.to_str().map(|s| s.to_owned()))
.and_then(|s| log::LevelFilter::from_str(&s).ok())
.unwrap_or(log::LevelFilter::Warn);
CombinedLogger::init(vec![
TermLogger::new(log::LevelFilter::Warn, Config::default()).unwrap(),
WriteLogger::new(log::LevelFilter::Info, Config::default(), File::create(&settings.log.file).unwrap()),
]).unwrap();
TermLogger::new(term_log_level, Config::default()).unwrap(),
WriteLogger::new(
log::LevelFilter::Info,
Config::default(),
File::create(&settings.log.file).unwrap(),
),
])
.unwrap();
// Set up panic handler to relay swish panic messages to the user
let settings_clone = settings.clone();
@ -123,18 +128,13 @@ The information below is intended for developers and testers.
msgbox::create("Voxygen has panicked", &msg, msgbox::IconType::ERROR);
}));
let mut global_state = GlobalState {
settings,
window,
};
let mut global_state = GlobalState { settings, window };
// Set up the initial play state
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(
&mut global_state,
))];
states.last().map(|current_state| {
log::info!("Started game with state '{}'", current_state.name())
});
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
states
.last()
.map(|current_state| log::info!("Started game with state '{}'", current_state.name()));
// What's going on here?
// ---------------------
@ -144,7 +144,10 @@ The information below is intended for developers and testers.
// The code below manages the state transfer logic automatically so that we don't have to
// re-engineer it for each menu we decide to add to the game.
let mut direction = Direction::Forwards;
while let Some(state_result) = states.last_mut().map(|last| last.play(direction, &mut global_state)){
while let Some(state_result) = states
.last_mut()
.map(|last| last.play(direction, &mut global_state))
{
// Implement state transfer logic
match state_result {
PlayStateResult::Shutdown => {
@ -156,28 +159,32 @@ The information below is intended for developers and testers.
global_state.on_play_state_changed();
});
}
},
}
PlayStateResult::Pop => {
direction = Direction::Backwards;
states.pop().map(|old_state| {
log::info!("Popped state '{}'", old_state.name());
global_state.on_play_state_changed();
});
},
}
PlayStateResult::Push(new_state) => {
direction = Direction::Forwards;
log::info!("Pushed state '{}'", new_state.name());
states.push(new_state);
global_state.on_play_state_changed();
},
}
PlayStateResult::Switch(mut new_state) => {
direction = Direction::Forwards;
states.last_mut().map(|old_state| {
log::info!("Switching to state '{}' from state '{}'", new_state.name(), old_state.name());
log::info!(
"Switching to state '{}' from state '{}'",
new_state.name(),
old_state.name()
);
mem::swap(old_state, &mut new_state);
global_state.on_play_state_changed();
});
},
}
}
}
}

View File

@ -1,15 +1,12 @@
mod ui;
use crate::{
window::{Event, Window},
session::SessionState,
window::{Event, Window},
Direction, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{
clock::Clock,
msg::ClientMsg,
};
use common::{clock::Clock, msg::ClientMsg};
use std::{cell::RefCell, rc::Rc, time::Duration};
use ui::CharSelectionUi;
use vek::*;
@ -50,7 +47,7 @@ impl PlayState for CharSelectionState {
match event {
Event::Close => {
return PlayStateResult::Shutdown;
},
}
// Pass events to ui
Event::Ui(event) => {
self.char_selection_ui.handle_event(event);
@ -63,23 +60,36 @@ impl PlayState for CharSelectionState {
global_state.window.renderer_mut().clear(BG_COLOR);
// Maintain the UI
for event in self.char_selection_ui.maintain(global_state.window.renderer_mut()) {
for event in self
.char_selection_ui
.maintain(global_state.window.renderer_mut())
{
match event {
ui::Event::Logout => {
return PlayStateResult::Pop;
},
}
ui::Event::Play => {
self.client.borrow_mut().postbox.send_message(ClientMsg::Character(self.char_selection_ui.character));
return PlayStateResult::Switch( Box::new(SessionState::new(&mut global_state.window, self.client.clone(), global_state.settings.clone())));
self.client
.borrow_mut()
.postbox
.send_message(ClientMsg::Character(self.char_selection_ui.character));
return PlayStateResult::Switch(Box::new(SessionState::new(
&mut global_state.window,
self.client.clone(),
global_state.settings.clone()
)));
}
}
}
// Draw the UI to the screen
self.char_selection_ui.render(global_state.window.renderer_mut());
self.char_selection_ui
.render(global_state.window.renderer_mut());
// Tick the client (currently only to keep the connection alive)
self.client.borrow_mut().tick(client::Input::default(), clock.get_last_delta())
self.client
.borrow_mut()
.tick(client::Input::default(), clock.get_last_delta())
.expect("Failed to tick the client");
self.client.borrow_mut().cleanup();

View File

@ -5,19 +5,8 @@ use crate::{
};
use common::{
assets,
comp::character::{Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon},
figure::Segment,
comp::character::{
Character,
Race,
Gender,
Head,
Chest,
Belt,
Pants,
Hand,
Foot,
Weapon,
}
};
use conrod_core::{
color,
@ -381,10 +370,12 @@ impl CharSelectionUi {
// Load fonts
let load_font = |filename, ui: &mut Ui| {
let fullpath: String = ["/voxygen/font", filename].concat();
ui.new_font(conrod_core::text::Font::from_bytes(
assets::load(fullpath.as_str())
.expect("Error loading file")
).unwrap())
ui.new_font(
conrod_core::text::Font::from_bytes(
assets::load(fullpath.as_str()).expect("Error loading file"),
)
.unwrap(),
)
};
let font_opensans = load_font("/OpenSans-Regular.ttf", &mut ui);
let font_metamorph = load_font("/Metamorphous-Regular.ttf", &mut ui);
@ -471,7 +462,7 @@ impl CharSelectionUi {
// Veloren Logo and Alpha Version
Image::new(self.imgs.v_logo)
.w_h(123.0*3.0, 35.0*3.0)
.w_h(123.0 * 3.0, 35.0 * 3.0)
.top_left_with_margins(30.0, 30.0)
.set(self.ids.v_logo, ui_widgets);
Text::new(version)

View File

@ -20,10 +20,7 @@ pub struct ClientInit {
rx: Receiver<Result<Client, Error>>,
}
impl ClientInit {
pub fn new(
connection_args: (String, u16, bool),
client_args: (comp::Player, u64),
) -> Self {
pub fn new(connection_args: (String, u16, bool), client_args: (comp::Player, u64)) -> Self {
let (server_address, default_port, prefer_ipv6) = connection_args;
let (player, view_distance) = client_args;

View File

@ -2,7 +2,6 @@ mod client_init;
mod start_singleplayer;
mod ui;
use start_singleplayer::StartSingleplayerState;
use super::char_selection::CharSelectionState;
use crate::{
window::{Event, Window},
@ -10,6 +9,7 @@ use crate::{
};
use client_init::{ClientInit, Error as InitError};
use common::{clock::Clock, comp};
use start_singleplayer::StartSingleplayerState;
use std::time::Duration;
use ui::{Event as MainMenuEvent, MainMenuUi};
use vek::*;
@ -86,10 +86,7 @@ impl PlayState for MainMenuState {
}
// Maintain the UI
for event in self
.main_menu_ui
.maintain(global_state)
{
for event in self.main_menu_ui.maintain(global_state) {
match event {
MainMenuEvent::LoginAttempt {
username,
@ -104,15 +101,12 @@ impl PlayState for MainMenuState {
// Don't try to connect if there is already a connection in progress
client_init = client_init.or(Some(ClientInit::new(
(server_address, DEFAULT_PORT, false),
(
comp::Player::new(username.clone()),
300,
),
(comp::Player::new(username.clone()), 300),
)));
},
}
MainMenuEvent::StartSingleplayer => {
return PlayStateResult::Push(Box::new(StartSingleplayerState::new()));
},
}
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
}
}

View File

@ -1,10 +1,9 @@
use common::comp;
use super::{DEFAULT_PORT, client_init::ClientInit};
use super::{client_init::ClientInit, DEFAULT_PORT};
use crate::{
menu::char_selection::CharSelectionState,
singleplayer::Singleplayer,
Direction, GlobalState, PlayState, PlayStateResult,
menu::char_selection::CharSelectionState, singleplayer::Singleplayer, Direction, GlobalState,
PlayState, PlayStateResult,
};
use common::comp;
pub struct StartSingleplayerState {
singleplayer: Singleplayer,
@ -28,10 +27,7 @@ impl PlayState for StartSingleplayerState {
let client_init = ClientInit::new(
(server_address.clone(), DEFAULT_PORT, false),
(
comp::Player::new(username.clone()),
300,
),
(comp::Player::new(username.clone()), 300),
);
// Client creation
@ -39,8 +35,8 @@ impl PlayState for StartSingleplayerState {
match client_init.poll() {
Some(Ok(client)) => break client,
// Should always work
Some(Err(err)) => unreachable!(),
_ => {},
Some(Err(err)) => unreachable!(),
_ => {}
}
};
@ -55,7 +51,7 @@ impl PlayState for StartSingleplayerState {
&mut global_state.window,
std::rc::Rc::new(std::cell::RefCell::new(client)),
)))
},
}
Direction::Backwards => PlayStateResult::Pop,
}
}

View File

@ -172,7 +172,7 @@ impl MainMenuUi {
.middle_of(ui_widgets.window)
.set(self.ids.bg, ui_widgets);
Image::new(self.imgs.v_logo)
.w_h(123.0*3.0, 35.0*3.0)
.w_h(123.0 * 3.0, 35.0 * 3.0)
.top_left_with_margins(30.0, 30.0)
.set(self.ids.v_logo, ui_widgets);
Text::new(version)
@ -373,7 +373,7 @@ impl MainMenuUi {
.label_y(Relative::Scalar(5.0))
.set(self.ids.login_button, ui_widgets);
} else {
if Button::image(self.imgs.button)
if Button::image(self.imgs.button)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.w_h(258.0, 68.0)

View File

@ -1,11 +1,9 @@
pub mod segment;
pub mod terrain;
mod vol;
// Crate
use crate::render::{
self,
Mesh,
};
use crate::render::{self, Mesh};
pub trait Meshable {
type Pipeline: render::Pipeline;

View File

@ -3,43 +3,20 @@ use vek::*;
// Project
use common::{
vol::{
Vox,
SizedVol,
ReadVol,
},
figure::Segment,
vol::{ReadVol, SizedVol, Vox},
};
// Crate
use crate::{
mesh::Meshable,
render::{
self,
Mesh,
Quad,
FigurePipeline,
},
mesh::{vol, Meshable},
render::{self, FigurePipeline, Mesh, Quad},
};
type FigureVertex = <FigurePipeline as render::Pipeline>::Vertex;
// Utility function
// TODO: Evaluate how useful this is
fn create_quad(
origin: Vec3<f32>,
unit_x: Vec3<f32>,
unit_y: Vec3<f32>,
norm: Vec3<f32>,
col: Rgb<f32>,
bone: u8,
) -> Quad<FigurePipeline> {
Quad::new(
FigureVertex::new(origin, norm, col, bone),
FigureVertex::new(origin + unit_x, norm, col, bone),
FigureVertex::new(origin + unit_x + unit_y, norm, col, bone),
FigureVertex::new(origin + unit_y, norm, col, bone),
)
fn create_vertex(origin: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>) -> FigureVertex {
FigureVertex::new(origin, norm, col, 0)
}
impl Meshable for Segment {
@ -50,97 +27,17 @@ impl Meshable for Segment {
let mut mesh = Mesh::new();
for pos in self.iter_positions() {
if let Some(col) = self
.get(pos)
.ok()
.and_then(|vox| vox.get_color())
{
if let Some(col) = self.get(pos).ok().and_then(|vox| vox.get_color()) {
let col = col.map(|e| e as f32 / 255.0);
// -x
if self.get(pos - Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_y(),
-Vec3::unit_y(),
Vec3::unit_z(),
-Vec3::unit_x(),
col,
0,
));
}
// +x
if self.get(pos + Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
col,
0,
));
}
// -y
if self.get(pos - Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32),
Vec3::unit_x(),
Vec3::unit_z(),
-Vec3::unit_y(),
col,
0,
));
}
// +y
if self.get(pos + Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
col,
0,
));
}
// -z
if self.get(pos - Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32),
Vec3::unit_y(),
Vec3::unit_x(),
-Vec3::unit_z(),
col,
0,
));
}
// +z
if self.get(pos + Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
col,
0,
));
}
vol::push_vox_verts(
&mut mesh,
self,
pos,
offs + pos.map(|e| e as f32),
col,
create_vertex,
);
}
}

View File

@ -3,45 +3,19 @@ use vek::*;
// Project
use common::{
vol::{
Vox,
SizedVol,
ReadVol,
},
volumes::dyna::Dyna,
terrain::Block,
vol::{ReadVol, SizedVol, Vox},
volumes::dyna::Dyna,
};
// Crate
use crate::{
mesh::Meshable,
render::{
self,
Mesh,
Quad,
TerrainPipeline,
},
mesh::{vol, Meshable},
render::{self, Mesh, Quad, TerrainPipeline},
};
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
// Utility function
// TODO: Evaluate how useful this is
fn create_quad(
origin: Vec3<f32>,
unit_x: Vec3<f32>,
unit_y: Vec3<f32>,
norm: Vec3<f32>,
col: Rgb<f32>,
) -> Quad<TerrainPipeline> {
Quad::new(
TerrainVertex::new(origin, norm, col),
TerrainVertex::new(origin + unit_x, norm, col),
TerrainVertex::new(origin + unit_x + unit_y, norm, col),
TerrainVertex::new(origin + unit_y, norm, col),
)
}
impl<M> Meshable for Dyna<Block, M> {
type Pipeline = TerrainPipeline;
type Supplement = ();
@ -52,95 +26,17 @@ impl<M> Meshable for Dyna<Block, M> {
for pos in self
.iter_positions()
.filter(|pos| pos.map(|e| e >= 1).reduce_and())
.filter(|pos| pos.map2(self.get_size(), |e, sz| e < sz as i32 - 1).reduce_and())
.filter(|pos| {
pos.map2(self.get_size(), |e, sz| e < sz as i32 - 1)
.reduce_and()
})
{
let offs = pos.map(|e| e as f32 - 1.0);
if let Some(col) = self
.get(pos)
.ok()
.and_then(|vox| vox.get_color())
{
if let Some(col) = self.get(pos).ok().and_then(|vox| vox.get_color()) {
let col = col.map(|e| e as f32 / 255.0);
// -x
if self.get(pos - Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs,
Vec3::unit_z(),
Vec3::unit_y(),
-Vec3::unit_x(),
col,
));
}
// +x
if self.get(pos + Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
col,
));
}
// -y
if self.get(pos - Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs,
Vec3::unit_x(),
Vec3::unit_z(),
-Vec3::unit_y(),
col,
));
}
// +y
if self.get(pos + Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
col,
));
}
// -z
if self.get(pos - Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs,
Vec3::unit_y(),
Vec3::unit_x(),
-Vec3::unit_z(),
col,
));
}
// +z
if self.get(pos + Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
col,
));
}
vol::push_vox_verts(&mut mesh, self, pos, offs, col, TerrainVertex::new);
}
}

183
voxygen/src/mesh/vol.rs Normal file
View File

@ -0,0 +1,183 @@
use vek::*;
use common::vol::{ReadVol, Vox};
use crate::render::{
mesh::{Mesh, Quad},
Pipeline,
};
/// Given a volume, a position and the cardinal directions, compute each vertex' AO value
/// `dirs` should be a slice of length 5 so that the sliding window of size 2 over the slice
/// yields each vertex' adjacent positions.
fn get_ao_quad<V: ReadVol>(vol: &V, pos: Vec3<i32>, dirs: &[Vec3<i32>]) -> Vec4<f32> {
dirs.windows(2)
.map(|offs| {
let (s1, s2) = (
vol.get(pos + offs[0])
.map(|v| !v.is_empty())
.unwrap_or(false),
vol.get(pos + offs[1])
.map(|v| !v.is_empty())
.unwrap_or(false),
);
if s1 && s2 {
0.0
} else {
let corner = vol
.get(pos + offs[0] + offs[1])
.map(|v| !v.is_empty())
.unwrap_or(false);
// Map both 1 and 2 neighbors to 0.5 occlusion
if s1 || s2 || corner {
0.5
} else {
1.0
}
}
})
.collect::<Vec4<f32>>()
}
// Utility function
fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>) -> P::Vertex>(
origin: Vec3<f32>,
unit_x: Vec3<f32>,
unit_y: Vec3<f32>,
norm: Vec3<f32>,
col: Rgb<f32>,
ao: Vec4<f32>,
vcons: &F,
) -> Quad<P> {
let ao_scale = 1.0;
let dark = col * (1.0 - ao_scale);
if ao[0] + ao[2] < ao[1] + ao[3] {
Quad::new(
vcons(origin + unit_y, norm, Rgb::lerp(dark, col, ao[3])),
vcons(origin, norm, Rgb::lerp(dark, col, ao[0])),
vcons(origin + unit_x, norm, Rgb::lerp(dark, col, ao[1])),
vcons(origin + unit_x + unit_y, norm, Rgb::lerp(dark, col, ao[2])),
)
} else {
Quad::new(
vcons(origin, norm, Rgb::lerp(dark, col, ao[0])),
vcons(origin + unit_x, norm, Rgb::lerp(dark, col, ao[1])),
vcons(origin + unit_x + unit_y, norm, Rgb::lerp(dark, col, ao[2])),
vcons(origin + unit_y, norm, Rgb::lerp(dark, col, ao[3])),
)
}
}
pub fn push_vox_verts<
V: ReadVol,
P: Pipeline,
F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>) -> P::Vertex,
>(
mesh: &mut Mesh<P>,
vol: &V,
pos: Vec3<i32>,
offs: Vec3<f32>,
col: Rgb<f32>,
vcons: F,
) {
let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z());
// -x
if vol
.get(pos - Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs,
Vec3::unit_z(),
Vec3::unit_y(),
-Vec3::unit_x(),
col,
get_ao_quad(vol, pos - Vec3::unit_x(), &[-z, -y, z, y, -z]),
&vcons,
));
}
// +x
if vol
.get(pos + Vec3::unit_x())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
col,
get_ao_quad(vol, pos + Vec3::unit_x(), &[-y, -z, y, z, -y]),
&vcons,
));
}
// -y
if vol
.get(pos - Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs,
Vec3::unit_x(),
Vec3::unit_z(),
-Vec3::unit_y(),
col,
get_ao_quad(vol, pos - Vec3::unit_y(), &[-x, -z, x, z, -x]),
&vcons,
));
}
// +y
if vol
.get(pos + Vec3::unit_y())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
col,
get_ao_quad(vol, pos + Vec3::unit_y(), &[-z, -x, z, x, -z]),
&vcons,
));
}
// -z
if vol
.get(pos - Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs,
Vec3::unit_y(),
Vec3::unit_x(),
-Vec3::unit_z(),
col,
get_ao_quad(vol, pos - Vec3::unit_z(), &[-y, -x, y, x, -y]),
&vcons,
));
}
// +z
if vol
.get(pos + Vec3::unit_z())
.map(|v| v.is_empty())
.unwrap_or(true)
{
mesh.push_quad(create_quad(
offs + Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
col,
get_ao_quad(vol, pos + Vec3::unit_z(), &[-x, -y, x, y, -x]),
&vcons,
));
}
}

View File

@ -1,14 +1,8 @@
// Library
use gfx::{
self,
traits::FactoryExt,
};
use gfx::{self, traits::FactoryExt};
// Local
use super::{
RenderError,
gfx_backend,
};
use super::{gfx_backend, RenderError};
/// A handle to a series of constants sitting on the GPU. This is used to hold information used in
/// the rendering process that does not change throughout a single render pass.
@ -31,7 +25,8 @@ impl<T: Copy + gfx::traits::Pod> Consts<T> {
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
vals: &[T],
) -> Result<(), RenderError> {
encoder.update_buffer(&self.buf, vals, 0)
encoder
.update_buffer(&self.buf, vals, 0)
.map_err(|err| RenderError::UpdateError(err))
}
}

View File

@ -75,11 +75,7 @@ pub struct Tri<P: Pipeline> {
}
impl<P: Pipeline> Tri<P> {
pub fn new(
a: P::Vertex,
b: P::Vertex,
c: P::Vertex,
) -> Self {
pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex) -> Self {
Self { a, b, c }
}
}
@ -93,12 +89,7 @@ pub struct Quad<P: Pipeline> {
}
impl<P: Pipeline> Quad<P> {
pub fn new(
a: P::Vertex,
b: P::Vertex,
c: P::Vertex,
d: P::Vertex,
) -> Self {
pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex, d: P::Vertex) -> Self {
Self { a, b, c, d }
}
}

View File

@ -9,33 +9,19 @@ mod util;
// Reexports
pub use self::{
consts::Consts,
mesh::{Mesh, Tri, Quad},
mesh::{Mesh, Quad, Tri},
model::Model,
texture::Texture,
renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
pipelines::{
Globals,
figure::{
FigurePipeline,
Locals as FigureLocals,
BoneData as FigureBoneData,
},
skybox::{
create_mesh as create_skybox_mesh,
SkyboxPipeline,
Locals as SkyboxLocals,
},
terrain::{
TerrainPipeline,
Locals as TerrainLocals,
},
figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals},
skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline},
terrain::{Locals as TerrainLocals, TerrainPipeline},
ui::{
create_quad as create_ui_quad,
create_tri as create_ui_tri,
Mode as UiMode,
UiPipeline,
create_quad as create_ui_quad, create_tri as create_ui_tri, Mode as UiMode, UiPipeline,
},
Globals,
},
renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
texture::Texture,
};
#[cfg(feature = "gl")]
@ -64,8 +50,5 @@ pub enum RenderError {
/// - `SkyboxPipeline`
/// - `FigurePipeline`
pub trait Pipeline {
type Vertex:
Clone +
gfx::traits::Pod +
gfx::pso::buffer::Structure<gfx::format::Format>;
type Vertex: Clone + gfx::traits::Pod + gfx::pso::buffer::Structure<gfx::format::Format>;
}

View File

@ -1,15 +1,8 @@
// Library
use gfx::{
self,
traits::FactoryExt,
};
use gfx::{self, traits::FactoryExt};
// Local
use super::{
mesh::Mesh,
Pipeline,
gfx_backend,
};
use super::{gfx_backend, mesh::Mesh, Pipeline};
/// Represents a mesh that has been sent to the GPU.
pub struct Model<P: Pipeline> {
@ -18,10 +11,7 @@ pub struct Model<P: Pipeline> {
}
impl<P: Pipeline> Model<P> {
pub fn new(
factory: &mut gfx_backend::Factory,
mesh: &Mesh<P>,
) -> Self {
pub fn new(factory: &mut gfx_backend::Factory, mesh: &Mesh<P>) -> Self {
Self {
vbuf: factory.create_vertex_buffer(mesh.vertices()),
slice: gfx::Slice {

View File

@ -1,25 +1,20 @@
// Library
use gfx::{
self,
gfx_constant_struct_meta,
// Macros
gfx_defines,
gfx_vertex_struct_meta,
gfx_constant_struct_meta,
gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
gfx_vertex_struct_meta,
};
use vek::*;
// Local
use super::{
super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthFmt},
Globals,
super::{
Pipeline,
TgtColorFmt,
TgtDepthFmt,
util::arr_to_mat,
},
};
gfx_defines! {

View File

@ -6,9 +6,9 @@ pub mod ui;
// Library
use gfx::{
self,
gfx_constant_struct_meta,
// Macros
gfx_defines,
gfx_constant_struct_meta,
gfx_impl_struct_meta,
};
use vek::*;

View File

@ -1,25 +1,19 @@
// Library
use gfx::{
self,
gfx_constant_struct_meta,
// Macros
gfx_defines,
gfx_vertex_struct_meta,
gfx_constant_struct_meta,
gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
gfx_vertex_struct_meta,
};
// Local
use super::{
super::{Mesh, Pipeline, Quad, TgtColorFmt, TgtDepthFmt},
Globals,
super::{
Pipeline,
TgtColorFmt,
TgtDepthFmt,
Mesh,
Quad,
},
};
gfx_defines! {

View File

@ -1,24 +1,20 @@
// Library
use gfx::{
self,
gfx_constant_struct_meta,
// Macros
gfx_defines,
gfx_vertex_struct_meta,
gfx_constant_struct_meta,
gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
gfx_vertex_struct_meta,
};
use vek::*;
// Local
use super::{
super::{Pipeline, TgtColorFmt, TgtDepthFmt},
Globals,
super::{
Pipeline,
TgtColorFmt,
TgtDepthFmt,
},
};
gfx_defines! {

View File

@ -1,20 +1,14 @@
use super::super::{Pipeline, Quad, TgtColorFmt, TgtDepthFmt, Tri};
use gfx::{
self,
// Macros
gfx_defines,
gfx_vertex_struct_meta,
gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
gfx_vertex_struct_meta,
};
use vek::*;
use super::super::{
Pipeline,
TgtColorFmt,
TgtDepthFmt,
Quad,
Tri,
};
gfx_defines! {
vertex Vertex {
@ -65,20 +59,20 @@ impl Mode {
}
}
pub fn create_quad(rect: Aabr<f32>, uv_rect: Aabr<f32>, color: Rgba<f32>, mode: Mode) -> Quad<UiPipeline> {
pub fn create_quad(
rect: Aabr<f32>,
uv_rect: Aabr<f32>,
color: Rgba<f32>,
mode: Mode,
) -> Quad<UiPipeline> {
let mode_val = mode.value();
let v = |pos, uv| {
Vertex {
pos,
uv,
color: color.into_array(),
mode: mode_val,
}
let v = |pos, uv| Vertex {
pos,
uv,
color: color.into_array(),
mode: mode_val,
};
let aabr_to_lbrt = |aabr: Aabr<f32>| (
aabr.min.x, aabr.min.y,
aabr.max.x, aabr.max.y,
);
let aabr_to_lbrt = |aabr: Aabr<f32>| (aabr.min.x, aabr.min.y, aabr.max.x, aabr.max.y);
let (l, b, r, t) = aabr_to_lbrt(rect);
let (uv_l, uv_b, uv_r, uv_t) = aabr_to_lbrt(uv_rect);
@ -90,15 +84,18 @@ pub fn create_quad(rect: Aabr<f32>, uv_rect: Aabr<f32>, color: Rgba<f32>, mode:
)
}
pub fn create_tri(tri: [[f32; 2]; 3], uv_tri: [[f32; 2]; 3], color: Rgba<f32>, mode: Mode) -> Tri<UiPipeline> {
pub fn create_tri(
tri: [[f32; 2]; 3],
uv_tri: [[f32; 2]; 3],
color: Rgba<f32>,
mode: Mode,
) -> Tri<UiPipeline> {
let mode_val = mode.value();
let v = |pos, uv| {
Vertex {
pos,
uv,
color: color.into_array(),
mode: mode_val,
}
let v = |pos, uv| Vertex {
pos,
uv,
color: color.into_array(),
mode: mode_val,
};
Tri::new(
v([tri[0][0], tri[0][1]], [uv_tri[0][0], uv_tri[0][1]]),

View File

@ -1,25 +1,18 @@
use vek::*;
use super::{
consts::Consts,
gfx_backend,
mesh::Mesh,
model::Model,
pipelines::{figure, skybox, terrain, ui, Globals},
texture::Texture,
Pipeline, RenderError,
};
use gfx::{
self,
traits::{Device, FactoryExt},
};
use image;
use super::{
consts::Consts,
mesh::Mesh,
model::Model,
texture::Texture,
Pipeline,
RenderError,
gfx_backend,
pipelines::{
Globals,
figure,
skybox,
terrain,
ui,
},
};
use vek::*;
/// Represents the format of the window's color target.
pub type TgtColorFmt = gfx::format::Rgba8;
@ -149,37 +142,30 @@ impl Renderer {
pub fn update_consts<T: Copy + gfx::traits::Pod>(
&mut self,
consts: &mut Consts<T>,
vals: &[T]
vals: &[T],
) -> Result<(), RenderError> {
consts.update(&mut self.encoder, vals)
}
/// Create a new model from the provided mesh.
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderError> {
Ok(Model::new(
&mut self.factory,
mesh,
))
Ok(Model::new(&mut self.factory, mesh))
}
/// Create a new texture from the provided image.
pub fn create_texture<P: Pipeline>(&mut self, image: &image::DynamicImage) -> Result<Texture<P>, RenderError> {
Texture::new(
&mut self.factory,
image,
)
pub fn create_texture<P: Pipeline>(
&mut self,
image: &image::DynamicImage,
) -> Result<Texture<P>, RenderError> {
Texture::new(&mut self.factory, image)
}
/// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the specified dimensions
pub fn create_dynamic_texture<P: Pipeline>(
&mut self,
dims: Vec2<u16>
dims: Vec2<u16>,
) -> Result<Texture<P>, RenderError> {
Texture::new_dynamic(
&mut self.factory,
dims.x,
dims.y,
)
Texture::new_dynamic(&mut self.factory, dims.x, dims.y)
}
/// Update a texture with the provided offset, size, and data
@ -188,14 +174,9 @@ impl Renderer {
texture: &Texture<P>,
offset: [u16; 2],
size: [u16; 2],
data: &[[u8; 4]]
data: &[[u8; 4]],
) -> Result<(), RenderError> {
texture.update(
&mut self.encoder,
offset,
size,
data,
)
texture.update(&mut self.encoder, offset, size, data)
}
/// Queue the rendering of the provided skybox model in the upcoming frame.
@ -273,7 +254,12 @@ impl Renderer {
&self.ui_pipeline.pso,
&ui::pipe::Data {
vbuf: model.vbuf.clone(),
scissor: gfx::Rect { x: min.x, y: min.y, w: max.x - min.x, h: max.y - min.y },
scissor: gfx::Rect {
x: min.x,
y: min.y,
w: max.x - min.x,
h: max.y - min.y,
},
tex: (tex.srv.clone(), tex.sampler.clone()),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
@ -298,7 +284,8 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>(
.map_err(|err| RenderError::PipelineError(gfx::PipelineStateError::Program(err)))?;
Ok(GfxPipeline {
pso: factory.create_pipeline_from_program(
pso: factory
.create_pipeline_from_program(
&program,
gfx::Primitive::TriangleList,
gfx::state::Rasterizer {
@ -310,14 +297,17 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>(
},
pipe,
)
// Do some funky things to work around an oddity in gfx's error ownership rules
.map_err(|err| RenderError::PipelineError(match err {
gfx::PipelineStateError::Program(err) =>
gfx::PipelineStateError::Program(err),
gfx::PipelineStateError::DescriptorInit(err) =>
gfx::PipelineStateError::DescriptorInit(err.into()),
gfx::PipelineStateError::DeviceCreate(err) =>
gfx::PipelineStateError::DeviceCreate(err),
}))?,
// Do some funky things to work around an oddity in gfx's error ownership rules
.map_err(|err| {
RenderError::PipelineError(match err {
gfx::PipelineStateError::Program(err) => gfx::PipelineStateError::Program(err),
gfx::PipelineStateError::DescriptorInit(err) => {
gfx::PipelineStateError::DescriptorInit(err.into())
}
gfx::PipelineStateError::DeviceCreate(err) => {
gfx::PipelineStateError::DeviceCreate(err)
}
})
})?,
})
}

View File

@ -2,29 +2,25 @@
use std::marker::PhantomData;
// Library
use gfx::{
self,
traits::Factory,
};
use image::{
DynamicImage,
GenericImageView,
};
use gfx::{self, traits::Factory};
use image::{DynamicImage, GenericImageView};
use vek::Vec2;
// Local
use super::{
RenderError,
Pipeline,
gfx_backend,
};
use super::{gfx_backend, Pipeline, RenderError};
type ShaderFormat = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb);
/// Represents an image that has been uploaded to the GPU.
pub struct Texture<P: Pipeline> {
pub tex: gfx::handle::Texture<gfx_backend::Resources, <ShaderFormat as gfx::format::Formatted>::Surface>,
pub srv: gfx::handle::ShaderResourceView<gfx_backend::Resources, <ShaderFormat as gfx::format::Formatted>::View>,
pub tex: gfx::handle::Texture<
gfx_backend::Resources,
<ShaderFormat as gfx::format::Formatted>::Surface,
>,
pub srv: gfx::handle::ShaderResourceView<
gfx_backend::Resources,
<ShaderFormat as gfx::format::Formatted>::View,
>,
pub sampler: gfx::handle::Sampler<gfx_backend::Resources>,
_phantom: PhantomData<P>,
}
@ -34,15 +30,16 @@ impl<P: Pipeline> Texture<P> {
factory: &mut gfx_backend::Factory,
image: &DynamicImage,
) -> Result<Self, RenderError> {
let (tex, srv) = factory.create_texture_immutable_u8::<ShaderFormat>(
gfx::texture::Kind::D2(
image.width() as u16,
image.height() as u16,
gfx::texture::AaMode::Single,
),
gfx::texture::Mipmap::Provided,
&[&image.to_rgba().into_raw()],
)
let (tex, srv) = factory
.create_texture_immutable_u8::<ShaderFormat>(
gfx::texture::Kind::D2(
image.width() as u16,
image.height() as u16,
gfx::texture::AaMode::Single,
),
gfx::texture::Mipmap::Provided,
&[&image.to_rgba().into_raw()],
)
.map_err(|err| RenderError::CombinedError(err))?;
Ok(Self {
@ -73,8 +70,12 @@ impl<P: Pipeline> Texture<P> {
)
.map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?;
let srv =
factory.view_texture_as_shader_resource::<ShaderFormat>(&tex, (0, 0), gfx::format::Swizzle::new())
let srv = factory
.view_texture_as_shader_resource::<ShaderFormat>(
&tex,
(0, 0),
gfx::format::Swizzle::new(),
)
.map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?;
Ok(Self {
@ -107,8 +108,10 @@ impl<P: Pipeline> Texture<P> {
mipmap: 0,
};
encoder
.update_texture::<<ShaderFormat as gfx::format::Formatted>::Surface, ShaderFormat>(&self.tex, None, info, data)
.map_err(|err| RenderError::TexUpdateError(err))
.update_texture::<<ShaderFormat as gfx::format::Formatted>::Surface, ShaderFormat>(
&self.tex, None, info, data,
)
.map_err(|err| RenderError::TexUpdateError(err))
}
/// Get dimensions of the represented image
pub fn get_dimensions(&self) -> Vec2<u16> {

View File

@ -38,12 +38,7 @@ impl Camera {
* Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x())
* Mat4::translation_3d(-self.focus);
let proj_mat = Mat4::perspective_rh_no(
self.fov,
self.aspect,
NEAR_PLANE,
FAR_PLANE,
);
let proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE);
// TODO: Make this more efficient
let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w());
@ -56,9 +51,7 @@ impl Camera {
// Wrap camera yaw
self.ori.x = (self.ori.x + delta.x) % (2.0 * PI);
// Clamp camera pitch to the vertical limits
self.ori.y = (self.ori.y + delta.y)
.min(PI / 2.0)
.max(-PI / 2.0);
self.ori.y = (self.ori.y + delta.y).min(PI / 2.0).max(-PI / 2.0);
// Wrap camera roll
self.ori.z = (self.ori.z + delta.z) % (2.0 * PI);
}
@ -70,15 +63,25 @@ impl Camera {
}
/// Get the focus position of the camera.
pub fn get_focus_pos(&self) -> Vec3<f32> { self.focus }
pub fn get_focus_pos(&self) -> Vec3<f32> {
self.focus
}
/// Set the focus position of the camera.
pub fn set_focus_pos(&mut self, focus: Vec3<f32>) { self.focus = focus; }
pub fn set_focus_pos(&mut self, focus: Vec3<f32>) {
self.focus = focus;
}
/// Get the aspect ratio of the camera.
pub fn get_aspect_ratio(&self) -> f32 { self.aspect }
pub fn get_aspect_ratio(&self) -> f32 {
self.aspect
}
/// Set the aspect ratio of the camera.
pub fn set_aspect_ratio(&mut self, aspect: f32) { self.aspect = aspect; }
pub fn set_aspect_ratio(&mut self, aspect: f32) {
self.aspect = aspect;
}
/// Get the orientation of the camera
pub fn get_orientation(&self) -> Vec3<f32> { self.ori }
pub fn get_orientation(&self) -> Vec3<f32> {
self.ori
}
}

View File

@ -1,51 +1,27 @@
use std::{
collections::HashMap,
f32,
use crate::{
anim::{
character::{CharacterSkeleton, IdleAnimation, RunAnimation},
Animation, Skeleton,
},
mesh::Meshable,
render::{
Consts, FigureBoneData, FigureLocals, FigurePipeline, Globals, Mesh, Model, Renderer,
},
Error,
};
use specs::{Entity as EcsEntity, Component, VecStorage, Join};
use vek::*;
use client::Client;
use common::{
assets,
comp::{
self,
character::{
Character,
Head,
Chest,
Belt,
Pants,
Hand,
Foot,
Weapon,
}
character::{Belt, Character, Chest, Foot, Hand, Head, Pants, Weapon},
},
figure::Segment,
msg,
assets,
};
use crate::{
Error,
render::{
Consts,
Globals,
Mesh,
Model,
Renderer,
FigurePipeline,
FigureBoneData,
FigureLocals,
},
anim::{
Animation,
Skeleton,
character::{
CharacterSkeleton,
RunAnimation,
IdleAnimation,
},
},
mesh::Meshable,
};
use specs::{Component, Entity as EcsEntity, Join, VecStorage};
use std::{collections::HashMap, f32};
use vek::*;
pub struct FigureCache {
models: HashMap<Character, (Model<FigurePipeline>, u64)>,
@ -64,45 +40,52 @@ impl FigureCache {
models: &'a mut HashMap<Character, (Model<FigurePipeline>, u64)>,
renderer: &mut Renderer,
tick: u64,
character: Character)
-> &'a (Model<FigurePipeline>, u64) {
character: Character,
) -> &'a (Model<FigurePipeline>, u64) {
match models.get_mut(&character) {
Some((model, last_used)) => {
*last_used = tick;
}
None => {
models.insert(character, ({
let bone_meshes = [
Some(Self::load_head(character.head)),
Some(Self::load_chest(character.chest)),
Some(Self::load_belt(character.belt)),
Some(Self::load_pants(character.pants)),
Some(Self::load_left_hand(character.hand)),
Some(Self::load_right_hand(character.hand)),
Some(Self::load_left_foot(character.foot)),
Some(Self::load_right_foot(character.foot)),
Some(Self::load_weapon(character.weapon)),
None,
None,
None,
None,
None,
None,
None,
];
models.insert(
character,
(
{
let bone_meshes = [
Some(Self::load_head(character.head)),
Some(Self::load_chest(character.chest)),
Some(Self::load_belt(character.belt)),
Some(Self::load_pants(character.pants)),
Some(Self::load_left_hand(character.hand)),
Some(Self::load_right_hand(character.hand)),
Some(Self::load_left_foot(character.foot)),
Some(Self::load_right_foot(character.foot)),
Some(Self::load_weapon(character.weapon)),
None,
None,
None,
None,
None,
None,
None,
];
let mut mesh = Mesh::new();
bone_meshes
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
.for_each(|(i, bone_mesh)| {
mesh.push_mesh_map(bone_mesh, |vert| {
vert.with_bone_idx(i as u8)
})
});
let mut mesh = Mesh::new();
bone_meshes
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
.for_each(|(i, bone_mesh)| {
mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8))
});
renderer.create_model(&mesh).unwrap()
}, tick));
renderer.create_model(&mesh).unwrap()
},
tick,
),
);
}
}
@ -111,76 +94,106 @@ impl FigureCache {
pub fn clean(&mut self, tick: u64) {
// TODO: Don't hard-code this
self.models.retain(|_, (_, last_used)| *last_used + 60 > tick);
self.models
.retain(|_, (_, last_used)| *last_used + 60 > tick);
}
fn load_mesh(filename: &'static str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
let fullpath: String = ["/voxygen/voxel/", filename].concat();
Segment::from(dot_vox::load_bytes(
assets::load(fullpath.as_str())
.expect("Error loading file")
.as_slice(),
).unwrap())
.generate_mesh(position)
Segment::from(
dot_vox::load_bytes(
assets::load(fullpath.as_str())
.expect("Error loading file")
.as_slice(),
)
.unwrap(),
)
.generate_mesh(position)
}
fn load_head(head: Head) -> Mesh<FigurePipeline> {
Self::load_mesh(match head {
Head::DefaultHead => "head.vox",
}, Vec3::new(-5.5, -7.0, -6.0))
Self::load_mesh(
match head {
Head::DefaultHead => "head.vox",
},
Vec3::new(-5.5, -7.0, -6.0),
)
}
fn load_chest(chest: Chest) -> Mesh<FigurePipeline> {
Self::load_mesh(match chest {
Chest::DefaultChest => "chest.vox",
}, Vec3::new(-2.5, -6.0, 0.0))
Self::load_mesh(
match chest {
Chest::DefaultChest => "chest.vox",
},
Vec3::new(-2.5, -6.0, 0.0),
)
}
fn load_belt(belt: Belt) -> Mesh<FigurePipeline> {
Self::load_mesh(match belt {
Belt::DefaultBelt => "belt.vox",
}, Vec3::new(-2.5, -5.0, 0.0))
Self::load_mesh(
match belt {
Belt::DefaultBelt => "belt.vox",
},
Vec3::new(-2.5, -5.0, 0.0),
)
}
fn load_pants(pants: Pants) -> Mesh<FigurePipeline> {
Self::load_mesh(match pants {
Pants::DefaultPants => "pants.vox",
}, Vec3::new(-2.5, -5.0, 0.0))
Self::load_mesh(
match pants {
Pants::DefaultPants => "pants.vox",
},
Vec3::new(-2.5, -5.0, 0.0),
)
}
fn load_left_hand(hand: Hand) -> Mesh<FigurePipeline> {
Self::load_mesh(match hand {
Hand::DefaultHand => "hand.vox",
}, Vec3::new(0.0, -2.0, -7.0))
Self::load_mesh(
match hand {
Hand::DefaultHand => "hand.vox",
},
Vec3::new(0.0, -2.0, -7.0),
)
}
fn load_right_hand(hand: Hand) -> Mesh<FigurePipeline> {
Self::load_mesh(match hand {
Hand::DefaultHand => "hand.vox",
}, Vec3::new(0.0, -2.0, -7.0))
Self::load_mesh(
match hand {
Hand::DefaultHand => "hand.vox",
},
Vec3::new(0.0, -2.0, -7.0),
)
}
fn load_left_foot(foot: Foot) -> Mesh<FigurePipeline> {
Self::load_mesh(match foot {
Foot::DefaultFoot => "foot.vox",
}, Vec3::new(-3.5, -2.5, -8.0))
Self::load_mesh(
match foot {
Foot::DefaultFoot => "foot.vox",
},
Vec3::new(-3.5, -2.5, -8.0),
)
}
fn load_right_foot(foot: Foot) -> Mesh<FigurePipeline> {
Self::load_mesh(match foot {
Foot::DefaultFoot => "foot.vox",
}, Vec3::new(-3.5, -2.5, -8.0))
Self::load_mesh(
match foot {
Foot::DefaultFoot => "foot.vox",
},
Vec3::new(-3.5, -2.5, -8.0),
)
}
fn load_weapon(weapon: Weapon) -> Mesh<FigurePipeline> {
Self::load_mesh(match weapon {
Weapon::Sword => "sword.vox",
// TODO actually match against other weapons and set the right model
_ => "sword.vox",
}, Vec3::new(0.0, 0.0, -4.0))
Self::load_mesh(
match weapon {
Weapon::Sword => "sword.vox",
// TODO actually match against other weapons and set the right model
_ => "sword.vox",
},
Vec3::new(0.0, 0.0, -4.0),
)
}
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
let time = client.state().get_time();
let ecs = client.state_mut().ecs_mut();
@ -190,14 +203,21 @@ impl FigureCache {
&ecs.read_storage::<comp::phys::Dir>(),
&ecs.read_storage::<comp::Character>(),
&ecs.read_storage::<comp::AnimationHistory>(),
).join() {
let state = self.states
)
.join()
{
let state = self
.states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
let target_skeleton = match animation_history.current {
comp::character::Animation::Idle => IdleAnimation::update_skeleton(&mut state.skeleton, time),
comp::character::Animation::Run => RunAnimation::update_skeleton(&mut state.skeleton, time),
comp::character::Animation::Idle => {
IdleAnimation::update_skeleton(&mut state.skeleton, time)
}
comp::character::Animation::Run => {
RunAnimation::update_skeleton(&mut state.skeleton, time)
}
};
state.skeleton.interpolate(&target_skeleton);
@ -205,26 +225,25 @@ impl FigureCache {
state.update(renderer, pos.0, dir.0);
}
self.states.retain(|entity, _| ecs.entities().is_alive(*entity));
self.states
.retain(|entity, _| ecs.entities().is_alive(*entity));
}
pub fn render(&mut self, renderer: &mut Renderer, client: &mut Client, globals: &Consts<Globals>) {
pub fn render(
&mut self,
renderer: &mut Renderer,
client: &mut Client,
globals: &Consts<Globals>,
) {
let tick = client.get_tick();
let ecs = client.state().ecs();
let models = &mut self.models;
for (entity, &character) in (
&ecs.entities(),
&ecs.read_storage::<comp::Character>(),
).join() {
for (entity, &character) in (&ecs.entities(), &ecs.read_storage::<comp::Character>()).join()
{
let model = Self::get_or_create_model(models, renderer, tick, character);
let state = self.states.get(&entity).unwrap();
renderer.render_figure(
&model.0,
globals,
&state.locals,
&state.bone_consts,
);
renderer.render_figure(&model.0, globals, &state.locals, &state.bone_consts);
}
}
}
@ -238,21 +257,24 @@ pub struct FigureState<S: Skeleton> {
impl<S: Skeleton> FigureState<S> {
pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
Self {
bone_consts: renderer.create_consts(&skeleton.compute_matrices()).unwrap(),
bone_consts: renderer
.create_consts(&skeleton.compute_matrices())
.unwrap(),
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
skeleton,
}
}
fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
let mat =
Mat4::<f32>::identity() *
Mat4::translation_3d(pos) *
Mat4::rotation_z(-dir.x.atan2(dir.y) + f32::consts::PI / 2.0);
let mat = Mat4::<f32>::identity()
* Mat4::translation_3d(pos)
* Mat4::rotation_z(-dir.x.atan2(dir.y) + f32::consts::PI / 2.0);
let locals = FigureLocals::new(mat);
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()).unwrap();
renderer
.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())
.unwrap();
}
}

View File

@ -2,35 +2,23 @@ pub mod camera;
pub mod figure;
pub mod terrain;
use vek::*;
use dot_vox;
use common::{
comp,
figure::Segment,
};
use client::Client;
use self::{camera::Camera, figure::FigureCache, terrain::Terrain};
use crate::{
anim::{
character::{CharacterSkeleton, RunAnimation},
Animation,
},
mesh::Meshable,
render::{
Consts,
Globals,
Model,
Renderer,
create_skybox_mesh, Consts, FigureLocals, Globals, Model, Renderer, SkyboxLocals,
SkyboxPipeline,
SkyboxLocals,
FigureLocals,
create_skybox_mesh,
},
window::Event,
mesh::Meshable, anim::{
Animation,
character::{CharacterSkeleton, RunAnimation},
},
};
use self::{
camera::Camera,
figure::FigureCache,
terrain::Terrain,
};
use client::Client;
use common::{comp, figure::Segment};
use dot_vox;
use vek::*;
// TODO: Don't hard-code this
const CURSOR_PAN_SCALE: f32 = 0.005;
@ -56,18 +44,12 @@ impl Scene {
let resolution = renderer.get_resolution().map(|e| e as f32);
Self {
globals: renderer
.create_consts(&[Globals::default()])
.unwrap(),
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
camera: Camera::new(resolution.x / resolution.y),
skybox: Skybox {
model: renderer
.create_model(&create_skybox_mesh())
.unwrap(),
locals: renderer
.create_consts(&[SkyboxLocals::default()])
.unwrap(),
model: renderer.create_model(&create_skybox_mesh()).unwrap(),
locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(),
},
terrain: Terrain::new(),
figure_cache: FigureCache::new(),
@ -75,10 +57,14 @@ impl Scene {
}
/// Get a reference to the scene's camera.
pub fn camera(&self) -> &Camera { &self.camera }
pub fn camera(&self) -> &Camera {
&self.camera
}
/// Get a mutable reference to the scene's camera.
pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera }
pub fn camera_mut(&mut self) -> &mut Camera {
&mut self.camera
}
/// Handle an incoming user input event (i.e: cursor moved, key pressed, window closed, etc.).
///
@ -89,17 +75,17 @@ impl Scene {
Event::Resize(dims) => {
self.camera.set_aspect_ratio(dims.x as f32 / dims.y as f32);
true
},
}
// Panning the cursor makes the camera rotate
Event::CursorPan(delta) => {
self.camera.rotate_by(Vec3::from(delta) * CURSOR_PAN_SCALE);
true
},
}
// Zoom the camera when a zoom event occurs
Event::Zoom(delta) => {
self.camera.zoom_by(delta * 0.3);
true
},
}
// All other events are unhandled
_ => false,
}
@ -123,15 +109,19 @@ impl Scene {
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents();
// Update global constants
renderer.update_consts(&mut self.globals, &[Globals::new(
view_mat,
proj_mat,
cam_pos,
self.camera.get_focus_pos(),
10.0,
client.state().get_time_of_day(),
client.state().get_time(),
)])
renderer
.update_consts(
&mut self.globals,
&[Globals::new(
view_mat,
proj_mat,
cam_pos,
self.camera.get_focus_pos(),
10.0,
client.state().get_time_of_day(),
client.state().get_time(),
)],
)
.expect("Failed to update global constants");
// Maintain the terrain
@ -147,11 +137,7 @@ impl Scene {
/// Render the scene using the provided `Renderer`
pub fn render(&mut self, renderer: &mut Renderer, client: &mut Client) {
// Render the skybox first (it appears over everything else so must be rendered first)
renderer.render_skybox(
&self.skybox.model,
&self.globals,
&self.skybox.locals,
);
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
// Render terrain and figures
self.terrain.render(renderer, &self.globals);

View File

@ -10,24 +10,12 @@ use vek::*;
// Project
use client::Client;
use common::{
terrain::TerrainMap,
volumes::vol_map::VolMapErr,
vol::SampleVol,
};
use common::{terrain::TerrainMap, vol::SampleVol, volumes::vol_map::VolMapErr};
// Crate
use crate::{
render::{
Consts,
Globals,
Mesh,
Model,
Renderer,
TerrainPipeline,
TerrainLocals,
},
mesh::Meshable,
render::{Consts, Globals, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline},
};
struct TerrainChunk {
@ -92,7 +80,11 @@ impl Terrain {
let current_tick = client.get_tick();
// Add any recently created or changed chunks to the list of chunks to be meshed
for pos in client.state().changes().new_chunks.iter()
for pos in client
.state()
.changes()
.new_chunks
.iter()
.chain(client.state().changes().changed_chunks.iter())
{
// TODO: ANOTHER PROBLEM HERE!
@ -139,8 +131,12 @@ impl Terrain {
// ambient occlusion and edge elision, we also need to borders of the chunk's
// neighbours too (hence the `- 1` and `+ 1`).
let aabb = Aabb {
min: todo.pos.map2(TerrainMap::chunk_size(), |e, sz| e * sz as i32 - 1),
max: todo.pos.map2(TerrainMap::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1),
min: todo
.pos
.map2(TerrainMap::chunk_size(), |e, sz| e * sz as i32 - 1),
max: todo
.pos
.map2(TerrainMap::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1),
};
// Copy out the chunk data we need to perform the meshing. We do this by taking a
@ -173,27 +169,35 @@ impl Terrain {
// It's the mesh we want, insert the newly finished model into the terrain model
// data structure (convert the mesh to a model first of course)
Some(todo) if response.started_tick == todo.started_tick => {
self.chunks.insert(response.pos, TerrainChunk {
model: renderer.create_model(&response.mesh).expect("Failed to upload chunk mesh to the GPU"),
locals: renderer.create_consts(&[TerrainLocals {
model_offs: response.pos.map2(TerrainMap::chunk_size(), |e, sz| e as f32 * sz as f32).into_array(),
}]).expect("Failed to upload chunk locals to the GPU"),
});
},
self.chunks.insert(
response.pos,
TerrainChunk {
model: renderer
.create_model(&response.mesh)
.expect("Failed to upload chunk mesh to the GPU"),
locals: renderer
.create_consts(&[TerrainLocals {
model_offs: response
.pos
.map2(TerrainMap::chunk_size(), |e, sz| {
e as f32 * sz as f32
})
.into_array(),
}])
.expect("Failed to upload chunk locals to the GPU"),
},
);
}
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
// since it's either out of date or no longer needed
_ => {},
_ => {}
}
}
}
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
for (_, chunk) in &self.chunks {
renderer.render_terrain_chunk(
&chunk.model,
globals,
&chunk.locals,
);
renderer.render_terrain_chunk(&chunk.model, globals, &chunk.locals);
}
}
}

View File

@ -1,23 +1,16 @@
use std::{cell::RefCell, rc::Rc, time::Duration};
use vek::*;
use common::clock::Clock;
use client::{
self,
Client,
};
use crate::{
Direction,
Error,
PlayState,
PlayStateResult,
GlobalState,
hud::{Event as HudEvent, Hud},
key_state::KeyState,
window::{Event, Key, Window},
render::Renderer,
scene::Scene,
settings::Settings,
hud::{Hud, Event as HudEvent},
window::{Event, Key, Window},
Direction, Error, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::clock::Clock;
use std::{cell::RefCell, rc::Rc, time::Duration};
use vek::*;
const FPS: u64 = 60;
@ -28,7 +21,6 @@ pub struct SessionState {
hud: Hud,
}
/// Represents an active game session (i.e: one that is being played)
impl SessionState {
/// Create a new `SessionState`
@ -44,7 +36,6 @@ impl SessionState {
}
}
// The background colour
const BG_COLOR: Rgba<f32> = Rgba {
r: 0.0,
@ -65,7 +56,11 @@ impl SessionState {
let dir_vec = self.key_state.dir_vec();
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
for event in self.client.borrow_mut().tick(client::Input { move_dir }, dt)? {
for event in self
.client
.borrow_mut()
.tick(client::Input { move_dir }, dt)?
{
match event {
client::Event::Chat(msg) => {
self.hud.new_message(msg);
@ -124,7 +119,6 @@ impl PlayState for SessionState {
loop {
// Handle window events
for event in global_state.window.fetch_events() {
// Pass all events to the ui first
if self.hud.handle_event(event.clone(), global_state) {
continue;
@ -132,7 +126,7 @@ impl PlayState for SessionState {
let _handled = match event {
Event::Close => {
return PlayStateResult::Shutdown;
},
}
// Toggle cursor grabbing
Event::KeyDown(Key::ToggleCursor) => {
global_state
@ -162,18 +156,24 @@ impl PlayState for SessionState {
.expect("Failed to tick the scene");
// Maintain the scene
self.scene.maintain(global_state.window.renderer_mut(), &mut self.client.borrow_mut());
self.scene.maintain(
global_state.window.renderer_mut(),
&mut self.client.borrow_mut(),
);
// Maintain the UI
for event in self.hud.maintain(global_state.window.renderer_mut(), clock.get_tps()) {
for event in self
.hud
.maintain(global_state.window.renderer_mut(), clock.get_tps())
{
match event {
HudEvent::SendMessage(msg) => {
// TODO: Handle result
self.client.borrow_mut().send_chat(msg);
},
}
HudEvent::Logout => return PlayStateResult::Pop,
HudEvent::Quit => {
return PlayStateResult::Shutdown;
},
}
}
}

View File

@ -1,14 +1,9 @@
use std::time::Duration;
use log::info;
use server::{Input, Event, Server};
use common::clock::Clock;
use std::{
thread,
thread::JoinHandle
};
use std::sync::mpsc::{
channel, Receiver, Sender, TryRecvError,
};
use log::info;
use server::{Event, Input, Server};
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::time::Duration;
use std::{thread, thread::JoinHandle};
const TPS: u64 = 30;
@ -49,11 +44,11 @@ fn run_server(rec: Receiver<Msg>) {
let mut clock = Clock::new();
// Create server
let mut server = Server::new()
.expect("Failed to create server instance");
let mut server = Server::new().expect("Failed to create server instance");
loop {
let events = server.tick(Input::default(), clock.get_last_delta())
let events = server
.tick(Input::default(), clock.get_last_delta())
.expect("Failed to tick server");
for event in events {

View File

@ -70,7 +70,7 @@ pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>) -> Vec<[u8; 4]> {
top: 1.0,
near: 0.0,
far: 1.0,
}) * Mat4::rotation_x(-std::f32::consts::PI / 2.0)
}) * Mat4::rotation_x(-std::f32::consts::PI / 2.0)
* Mat4::scaling_3d([2.0 / w, 2.0 / h, 2.0 / d])
* Mat4::translation_3d([-w / 2.0, -h / 2.0, -d / 2.0]);
Voxel { mvp }.draw::<rasterizer::Triangles<_>, _>(

View File

@ -1,46 +1,30 @@
mod widgets;
mod graphic;
mod util;
mod widgets;
pub use widgets::toggle_button::ToggleButton;
pub use graphic::Graphic;
pub(self) use util::{srgb_to_linear, linear_to_srgb};
pub(self) use util::{linear_to_srgb, srgb_to_linear};
pub use widgets::toggle_button::ToggleButton;
use graphic::{
GraphicCache,
Id as GraphicId,
};
use conrod_core::{
Ui as CrUi,
UiBuilder,
UiCell,
text::{
Font,
GlyphCache,
font::Id as FontId,
},
image::{Map, Id as ImgId},
widget::{Id as WidgId, id::Generator},
render::Primitive,
event::Input,
input::{touch::Touch, Widget, Motion, Button},
};
use vek::*;
use crate::{
Error,
render::{
RenderError,
Renderer,
Model,
Mesh,
Texture,
create_ui_quad, create_ui_tri, Mesh, Model, RenderError, Renderer, Texture, UiMode,
UiPipeline,
UiMode,
create_ui_quad,
create_ui_tri,
},
window::Window,
Error,
};
use conrod_core::{
event::Input,
image::{Id as ImgId, Map},
input::{touch::Touch, Button, Motion, Widget},
render::Primitive,
text::{font::Id as FontId, Font, GlyphCache},
widget::{id::Generator, Id as WidgId},
Ui as CrUi, UiBuilder, UiCell,
};
use graphic::{GraphicCache, Id as GraphicId};
use vek::*;
#[derive(Debug)]
pub enum UiError {
@ -66,19 +50,23 @@ impl Event {
winit::Window::get_hidpi_factor(&self.0) as _
}
}
convert_event!(event, &WindowRef(window.window())).map(|input| {
Self(input)
})
convert_event!(event, &WindowRef(window.window())).map(|input| Self(input))
}
pub fn is_keyboard_or_mouse(&self) -> bool {
match self.0 {
Input::Press(_) | Input::Release(_) | Input::Motion(_) | Input::Touch(_) | Input::Text(_) => true,
Input::Press(_)
| Input::Release(_)
| Input::Motion(_)
| Input::Touch(_)
| Input::Text(_) => true,
_ => false,
}
}
pub fn is_keyboard(&self) -> bool {
match self.0 {
Input::Press(Button::Keyboard(_)) | Input::Release(Button::Keyboard(_)) | Input::Text(_) => true,
Input::Press(Button::Keyboard(_))
| Input::Release(Button::Keyboard(_))
| Input::Text(_) => true,
_ => false,
}
}
@ -113,11 +101,21 @@ impl Cache {
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
})
}
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> { &self.glyph_cache_tex }
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) { (&mut self.glyph_cache, &self.glyph_cache_tex) }
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> { &self.graphic_cache_tex }
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) { (&mut self.graphic_cache, &self.graphic_cache_tex) }
pub fn new_graphic(&mut self, graphic: Graphic) -> GraphicId { self.graphic_cache.new_graphic(graphic) }
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> {
&self.glyph_cache_tex
}
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) {
(&mut self.glyph_cache, &self.glyph_cache_tex)
}
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> {
&self.graphic_cache_tex
}
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) {
(&mut self.graphic_cache, &self.graphic_cache_tex)
}
pub fn new_graphic(&mut self, graphic: Graphic) -> GraphicId {
self.graphic_cache.new_graphic(graphic)
}
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
self.graphic_cache.clear_cache(new_size);
self.graphic_cache_tex = renderer.create_dynamic_texture(new_size).unwrap();
@ -190,7 +188,9 @@ impl Scale {
match self.mode {
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
ScaleMode::DpiFactor => 1.0,
ScaleMode::RelativeToWindow(dims) => (self.window_dims.x / dims.x).min(self.window_dims.y / dims.y),
ScaleMode::RelativeToWindow(dims) => {
(self.window_dims.x / dims.x).min(self.window_dims.y / dims.y)
}
}
}
// Calculate factor to transform between physical coordinates and our scaled coordinates
@ -277,35 +277,36 @@ impl Ui {
// Get whether a widget besides the window is capturing the mouse
pub fn no_widget_capturing_mouse(&self) -> bool {
self.ui.global_input().current.widget_capturing_mouse.filter(|id| id != &self.ui.window ).is_none()
self.ui
.global_input()
.current
.widget_capturing_mouse
.filter(|id| id != &self.ui.window)
.is_none()
}
pub fn handle_event(&mut self, event: Event) {
match event.0 {
Input::Resize(w, h) => self.window_resized = Some(Vec2::new(w, h)),
Input::Touch(touch) => self.ui.handle_event(
Input::Touch(Touch {
xy: self.scale.scale_point(touch.xy.into()).into_array(),
..touch
})
),
Input::Motion(motion) => self.ui.handle_event(
Input::Motion( match motion {
Motion::MouseCursor { x, y } => {
let (x, y) = self.scale.scale_point(Vec2::new(x, y)).into_tuple();
Motion::MouseCursor { x, y }
}
Motion::MouseRelative { x, y } => {
let (x, y) = self.scale.scale_point(Vec2::new(x, y)).into_tuple();
Motion::MouseRelative { x, y }
}
Motion::Scroll { x, y } => {
let (x, y) = self.scale.scale_point(Vec2::new(x, y)).into_tuple();
Motion::Scroll { x, y }
}
_ => motion,
})
),
Input::Touch(touch) => self.ui.handle_event(Input::Touch(Touch {
xy: self.scale.scale_point(touch.xy.into()).into_array(),
..touch
})),
Input::Motion(motion) => self.ui.handle_event(Input::Motion(match motion {
Motion::MouseCursor { x, y } => {
let (x, y) = self.scale.scale_point(Vec2::new(x, y)).into_tuple();
Motion::MouseCursor { x, y }
}
Motion::MouseRelative { x, y } => {
let (x, y) = self.scale.scale_point(Vec2::new(x, y)).into_tuple();
Motion::MouseRelative { x, y }
}
Motion::Scroll { x, y } => {
let (x, y) = self.scale.scale_point(Vec2::new(x, y)).into_tuple();
Motion::Scroll { x, y }
}
_ => motion,
})),
_ => self.ui.handle_event(event.0),
}
}
@ -338,7 +339,8 @@ impl Ui {
macro_rules! switch_to_plain_state {
() => {
if let State::Image = current_state {
self.draw_commands.push(DrawCommand::image(renderer.create_model(&mesh).unwrap()));
self.draw_commands
.push(DrawCommand::image(renderer.create_model(&mesh).unwrap()));
mesh.clear();
current_state = State::Plain;
}
@ -348,7 +350,12 @@ impl Ui {
let p_scale_factor = self.scale.scale_factor_physical();
while let Some(prim) = primitives.next() {
let Primitive {kind, scizzor, id: _id, rect} = prim;
let Primitive {
kind,
scizzor,
id: _id,
rect,
} = prim;
// Check for a change in the scizzor
let new_scizzor = {
@ -366,17 +373,15 @@ impl Ui {
max: Vec2 {
x: ((min_x + w) * p_scale_factor) as u16,
y: ((min_y + h) * p_scale_factor) as u16,
}
},
}
.intersection(window_scizzor)
};
if new_scizzor != current_scizzor {
// Finish the current command
self.draw_commands.push(match current_state {
State::Plain =>
DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
State::Image =>
DrawCommand::image(renderer.create_model(&mesh).unwrap()),
State::Plain => DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
State::Image => DrawCommand::image(renderer.create_model(&mesh).unwrap()),
});
mesh.clear();
@ -398,8 +403,15 @@ impl Ui {
use conrod_core::render::PrimitiveKind;
match kind {
PrimitiveKind::Image { image_id, color, source_rect } => {
let graphic_id = self.image_map.get(&image_id).expect("Image does not exist in image map");
PrimitiveKind::Image {
image_id,
color,
source_rect,
} => {
let graphic_id = self
.image_map
.get(&image_id)
.expect("Image does not exist in image map");
let (graphic_cache, cache_tex) = self.cache.graphic_cache_mut_and_tex();
match graphic_cache.get_graphic(*graphic_id) {
@ -409,13 +421,15 @@ impl Ui {
// Switch to the `Image` state for this image if we're not in it already.
if let State::Plain = current_state {
self.draw_commands.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap()));
self.draw_commands
.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap()));
mesh.clear();
current_state = State::Image;
}
let color = srgb_to_linear(color.unwrap_or(conrod_core::color::WHITE).to_fsa().into());
let color = srgb_to_linear(
color.unwrap_or(conrod_core::color::WHITE).to_fsa().into(),
);
let resolution = Vec2::new(
(rect.w() * p_scale_factor) as u16,
@ -424,32 +438,44 @@ impl Ui {
// Transform the source rectangle into uv coordinate
// TODO: make sure this is right
let source_aabr = {
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0);/*match source_rect {
Some(src_rect) => {
let (l, r, b, t) = src_rect.l_r_b_t();
((l / image_w) as f32,
(r / image_w) as f32,
(b / image_h) as f32,
(t / image_h) as f32)
}
None => (0.0, 1.0, 0.0, 1.0),
};*/
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0); /*match source_rect {
Some(src_rect) => {
let (l, r, b, t) = src_rect.l_r_b_t();
((l / image_w) as f32,
(r / image_w) as f32,
(b / image_h) as f32,
(t / image_h) as f32)
}
None => (0.0, 1.0, 0.0, 1.0),
};*/
Aabr {
min: Vec2::new(uv_l, uv_b),
max: Vec2::new(uv_r, uv_t),
}
};
let (cache_w, cache_h) = cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
let (cache_w, cache_h) =
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
// Cache graphic at particular resolution
let uv_aabr = match graphic_cache.cache_res(*graphic_id, resolution, source_aabr, |aabr, data| {
let offset = aabr.min.into_array();
let size = aabr.size().into_array();
renderer.update_texture(cache_tex, offset, size, &data);
}) {
let uv_aabr = match graphic_cache.cache_res(
*graphic_id,
resolution,
source_aabr,
|aabr, data| {
let offset = aabr.min.into_array();
let size = aabr.size().into_array();
renderer.update_texture(cache_tex, offset, size, &data);
},
) {
Some(aabr) => Aabr {
min: Vec2::new(aabr.min.x as f32 / cache_w, aabr.max.y as f32 / cache_h),
max: Vec2::new(aabr.max.x as f32 / cache_w, aabr.min.y as f32 / cache_h),
min: Vec2::new(
aabr.min.x as f32 / cache_w,
aabr.max.y as f32 / cache_h,
),
max: Vec2::new(
aabr.max.x as f32 / cache_w,
aabr.min.y as f32 / cache_h,
),
},
None => continue,
};
@ -460,12 +486,16 @@ impl Ui {
color,
UiMode::Image,
));
}
PrimitiveKind::Text { color, text, font_id } => {
PrimitiveKind::Text {
color,
text,
font_id,
} => {
switch_to_plain_state!();
// Get screen width and height
let (screen_w, screen_h) = renderer.get_resolution().map(|e| e as f32).into_tuple();
let (screen_w, screen_h) =
renderer.get_resolution().map(|e| e as f32).into_tuple();
// Calculate dpi factor
let dpi_factor = screen_w / ui.win_w as f32;
@ -476,39 +506,41 @@ impl Ui {
glyph_cache.queue_glyph(font_id.index(), glyph.clone());
}
glyph_cache.cache_queued(|rect, data| {
let offset = [rect.min.x as u16, rect.min.y as u16];
let size = [rect.width() as u16, rect.height() as u16];
glyph_cache
.cache_queued(|rect, data| {
let offset = [rect.min.x as u16, rect.min.y as u16];
let size = [rect.width() as u16, rect.height() as u16];
let new_data = data.iter().map(|x| [255, 255, 255, *x]).collect::<Vec<[u8; 4]>>();
let new_data = data
.iter()
.map(|x| [255, 255, 255, *x])
.collect::<Vec<[u8; 4]>>();
renderer.update_texture(cache_tex, offset, size, &new_data);
}).unwrap();
renderer.update_texture(cache_tex, offset, size, &new_data);
})
.unwrap();
let color = srgb_to_linear(color.to_fsa().into());
for g in positioned_glyphs {
if let Ok(Some((uv_rect, screen_rect))) = glyph_cache.rect_for(font_id.index(), g) {
if let Ok(Some((uv_rect, screen_rect))) =
glyph_cache.rect_for(font_id.index(), g)
{
let uv = Aabr {
min: Vec2::new(uv_rect.min.x, uv_rect.max.y),
max: Vec2::new(uv_rect.max.x, uv_rect.min.y),
};
let rect = Aabr {
min: Vec2::new(
(screen_rect.min.x as f32 / screen_w - 0.5) * 2.0,
(screen_rect.min.x as f32 / screen_w - 0.5) * 2.0,
(screen_rect.max.y as f32 / screen_h - 0.5) * -2.0,
),
max: Vec2::new(
(screen_rect.max.x as f32 / screen_w - 0.5) * 2.0,
(screen_rect.max.x as f32 / screen_w - 0.5) * 2.0,
(screen_rect.min.y as f32 / screen_h - 0.5) * -2.0,
),
};
mesh.push_quad(create_ui_quad(
rect,
uv,
color,
UiMode::Text,
));
mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text));
}
}
}
@ -545,16 +577,13 @@ impl Ui {
let p2 = Vec2::new(vx(tri[1][0]), vy(tri[1][1]));
let p3 = Vec2::new(vx(tri[2][0]), vy(tri[2][1]));
// If triangle is clockwise reverse it
let (v1, v2): (Vec3<f32>, Vec3<f32>) = ((p2 - p1).into(), (p3 - p1).into());
let triangle = if v1.cross(v2).z > 0.0 {[
p1.into_array(),
p2.into_array(),
p3.into_array(),
]} else {[
p2.into_array(),
p1.into_array(),
p3.into_array(),
]};
let (v1, v2): (Vec3<f32>, Vec3<f32>) =
((p2 - p1).into(), (p3 - p1).into());
let triangle = if v1.cross(v2).z > 0.0 {
[p1.into_array(), p2.into_array(), p3.into_array()]
} else {
[p2.into_array(), p1.into_array(), p3.into_array()]
};
mesh.push_tri(create_ui_tri(
triangle,
[[0.0; 2]; 3],
@ -562,21 +591,17 @@ impl Ui {
UiMode::Geometry,
));
}
}
_ => {}
// TODO: Add this
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
// Other uneeded for now
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
_ => {} // TODO: Add this
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
// Other uneeded for now
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
}
}
// Enter the final command
self.draw_commands.push(match current_state {
State::Plain =>
DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
State::Image =>
DrawCommand::image(renderer.create_model(&mesh).unwrap()),
State::Plain => DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
State::Image => DrawCommand::image(renderer.create_model(&mesh).unwrap()),
});
// Handle window resizing
@ -584,7 +609,8 @@ impl Ui {
self.scale.window_resized(new_dims, renderer);
let (w, h) = self.scale.scaled_window_size().into_tuple();
self.ui.handle_event(Input::Resize(w, h));
self.cache.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
self.cache
.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
// TODO: probably need to resize glyph cache, see conrod's gfx backend for reference
}
}
@ -599,12 +625,8 @@ impl Ui {
}
DrawCommand::Draw { kind, model } => {
let tex = match kind {
DrawKind::Image => {
self.cache.graphic_cache_tex()
}
DrawKind::Plain => {
self.cache.glyph_cache_tex()
}
DrawKind::Image => self.cache.graphic_cache_tex(),
DrawKind::Plain => self.cache.glyph_cache_tex(),
};
renderer.render_ui_element(&model, &tex, scissor);
}
@ -617,6 +639,9 @@ fn default_scissor(renderer: &mut Renderer) -> Aabr<u16> {
let (screen_w, screen_h) = renderer.get_resolution().map(|e| e as u16).into_tuple();
Aabr {
min: Vec2 { x: 0, y: 0 },
max: Vec2 { x: screen_w, y: screen_h }
max: Vec2 {
x: screen_w,
y: screen_h,
},
}
}
}

View File

@ -1,13 +1,7 @@
use conrod_core::{
widget::{self, button},
image,
WidgetCommon,
Widget,
Sizeable,
Color,
Rect,
Positionable,
widget_ids,
widget::{self, button},
widget_ids, Color, Positionable, Rect, Sizeable, Widget, WidgetCommon,
};
#[derive(Clone, WidgetCommon)]
@ -88,7 +82,9 @@ impl Widget for ToggleButton {
type Event = bool;
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State { ids: Ids::new(id_gen) }
State {
ids: Ids::new(id_gen),
}
}
fn style(&self) -> Self::Style {
@ -96,8 +92,19 @@ impl Widget for ToggleButton {
}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs{ id, state, ui, rect, .. } = args;
let ToggleButton { mut value, f_image, t_image, .. } = self;
let widget::UpdateArgs {
id,
state,
ui,
rect,
..
} = args;
let ToggleButton {
mut value,
f_image,
t_image,
..
} = self;
// Check if button was clicked
// (can't use .set().was_clicked() because we are changing the image and this is after setting the widget which causes flickering as it takes a frame to change after the mouse button is lifted)
if ui.widget_input(state.ids.button).clicks().left().count() % 2 == 1 {

View File

@ -1,6 +1,7 @@
use crate::{
render::{Renderer, TgtColorFmt, TgtDepthFmt},
ui, Error, settings::Settings,
settings::Settings,
ui, Error,
};
use std::collections::HashMap;
use vek::*;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
world/.gitignore vendored
View File

@ -1,3 +0,0 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -1,15 +1,11 @@
// Library
use vek::*;
use noise::{NoiseFn, Perlin};
use vek::*;
// Project
use common::{
vol::{Vox, SizedVol, WriteVol},
terrain::{
Block,
TerrainChunk,
TerrainChunkMeta,
},
terrain::{Block, TerrainChunk, TerrainChunkMeta},
vol::{SizedVol, Vox, WriteVol},
};
#[derive(Debug)]
@ -30,7 +26,7 @@ impl World {
let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void());
let air = Block::empty();
let stone = Block::new(1, Rgb::new(200, 220, 255));
let stone = Block::new(1, Rgb::new(200, 220, 255));
let grass = Block::new(2, Rgb::new(75, 150, 0));
//let grass = Block::new(2, Rgb::new(50, 255, 0));
let dirt = Block::new(3, Rgb::new(128, 90, 0));
@ -47,20 +43,24 @@ impl World {
let small_freq = 1.0 / 32.0;
let small_ampl = 6.0;
let offs = 32.0;
let height =
perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl
+ perlin_nz.get(Vec2::from(wposf * small_freq).into_array()) * small_ampl
+ offs;
chunk.set(lpos, if wposf.z < height - 4.0 {
stone
} else if wposf.z < height - 1.0 {
dirt
} else if wposf.z < height {
grass
} else {
air
}).unwrap();
chunk
.set(
lpos,
if wposf.z < height - 4.0 {
stone
} else if wposf.z < height - 1.0 {
dirt
} else if wposf.z < height {
grass
} else {
air
},
)
.unwrap();
}
chunk