mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added server-cli, UI tests
Former-commit-id: 93bf5b39138920aa7a4a773a8247d716866c4a05
This commit is contained in:
parent
a1617e7b5c
commit
c16a257ae3
@ -3,6 +3,7 @@ members = [
|
|||||||
"common",
|
"common",
|
||||||
"client",
|
"client",
|
||||||
"server",
|
"server",
|
||||||
|
"server-cli",
|
||||||
"voxygen",
|
"voxygen",
|
||||||
"world",
|
"world",
|
||||||
]
|
]
|
||||||
|
3
server-cli/.gitignore
vendored
Normal file
3
server-cli/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
12
server-cli/Cargo.toml
Normal file
12
server-cli/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "veloren-server-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
server = { package = "veloren-server", path = "../server" }
|
||||||
|
common = { package = "veloren-common", path = "../common" }
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
pretty_env_logger = "0.3"
|
35
server-cli/src/main.rs
Normal file
35
server-cli/src/main.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Standard
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
// Library
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
// Project
|
||||||
|
use server::{self, Server};
|
||||||
|
use common::clock::Clock;
|
||||||
|
|
||||||
|
const FPS: u64 = 60;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Init logging
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
|
info!("Starting server-cli...");
|
||||||
|
|
||||||
|
// Set up an fps clock
|
||||||
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
|
// Create server
|
||||||
|
let mut server = Server::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
server.tick(server::Input {}, clock.get_last_delta())
|
||||||
|
.expect("Failed to tick server");
|
||||||
|
|
||||||
|
// Clean up the server after a tick
|
||||||
|
server.cleanup();
|
||||||
|
|
||||||
|
// Wait for the next tick
|
||||||
|
clock.tick(Duration::from_millis(1000 / FPS));
|
||||||
|
}
|
||||||
|
}
|
@ -62,4 +62,10 @@ impl Server {
|
|||||||
// Finish the tick, pass control back to the frontend (step 6)
|
// Finish the tick, pass control back to the frontend (step 6)
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clean up the server after a tick
|
||||||
|
pub fn cleanup(&mut self) {
|
||||||
|
// Cleanup the local state
|
||||||
|
self.state.cleanup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,3 +29,4 @@ lazy_static = "1.1"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.3"
|
pretty_env_logger = "0.3"
|
||||||
dot_vox = "1.0"
|
dot_vox = "1.0"
|
||||||
|
image = "0.21"
|
||||||
|
17
voxygen/shaders/ui.frag
Normal file
17
voxygen/shaders/ui.frag
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 f_pos;
|
||||||
|
in vec2 f_uv;
|
||||||
|
|
||||||
|
layout (std140)
|
||||||
|
uniform u_locals {
|
||||||
|
vec4 bounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform sampler2D u_tex;
|
||||||
|
|
||||||
|
out vec4 tgt_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
tgt_color = texture(u_tex, f_uv);
|
||||||
|
}
|
22
voxygen/shaders/ui.vert
Normal file
22
voxygen/shaders/ui.vert
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 v_pos;
|
||||||
|
in vec2 v_uv;
|
||||||
|
|
||||||
|
layout (std140)
|
||||||
|
uniform u_locals {
|
||||||
|
vec4 bounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform sampler2D u_tex;
|
||||||
|
|
||||||
|
out vec3 f_pos;
|
||||||
|
out vec2 f_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_uv = v_uv;
|
||||||
|
f_pos = vec3(vec2(bounds.x, bounds.y) + v_pos.xy * vec2(bounds.z, bounds.w), 0);
|
||||||
|
f_pos.xy = vec2(f_pos.x * 2.0 - 1.0, f_pos.y * -2.0 + 1.0);
|
||||||
|
|
||||||
|
gl_Position = vec4(f_pos, 1);
|
||||||
|
}
|
@ -7,6 +7,7 @@ pub mod mesh;
|
|||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
pub mod ui;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
@ -66,18 +67,20 @@ fn main() {
|
|||||||
// Init logging
|
// Init logging
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
// Set up the initial play state
|
|
||||||
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(TitleState::new())];
|
|
||||||
states.last().map(|current_state| {
|
|
||||||
log::info!("Started game with state '{}'", current_state.name())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up the global state
|
// Set up the global state
|
||||||
let mut global_state = GlobalState {
|
let mut global_state = GlobalState {
|
||||||
window: Window::new()
|
window: Window::new()
|
||||||
.expect("Failed to create window"),
|
.expect("Failed to create window"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set up the initial play state
|
||||||
|
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(TitleState::new(
|
||||||
|
&mut global_state.window.renderer_mut(),
|
||||||
|
))];
|
||||||
|
states.last().map(|current_state| {
|
||||||
|
log::info!("Started game with state '{}'", current_state.name())
|
||||||
|
});
|
||||||
|
|
||||||
// What's going on here?
|
// What's going on here?
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// The state system used by Voxygen allows for the easy development of stack-based menus.
|
// The state system used by Voxygen allows for the easy development of stack-based menus.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Library
|
// Library
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use image;
|
||||||
|
|
||||||
// Crate
|
// Crate
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -8,14 +9,28 @@ use crate::{
|
|||||||
GlobalState,
|
GlobalState,
|
||||||
window::Event,
|
window::Event,
|
||||||
session::SessionState,
|
session::SessionState,
|
||||||
|
render::Renderer,
|
||||||
|
ui::{
|
||||||
|
Ui,
|
||||||
|
element::{
|
||||||
|
Widget,
|
||||||
|
image::Image,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TitleState;
|
pub struct TitleState {
|
||||||
|
ui: Ui,
|
||||||
|
}
|
||||||
|
|
||||||
impl TitleState {
|
impl TitleState {
|
||||||
/// Create a new `TitleState`
|
/// Create a new `TitleState`
|
||||||
pub fn new() -> Self {
|
pub fn new(renderer: &mut Renderer) -> Self {
|
||||||
Self
|
let img = Image::new(renderer, &image::open(concat!(env!("CARGO_MANIFEST_DIR"), "/test_assets/test.png")).unwrap()).unwrap();
|
||||||
|
let widget = Widget::new(renderer, img).unwrap();
|
||||||
|
Self {
|
||||||
|
ui: Ui::new(renderer, widget).unwrap(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +56,12 @@ impl PlayState for TitleState {
|
|||||||
// Clear the screen
|
// Clear the screen
|
||||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||||
|
|
||||||
|
// Maintain the UI
|
||||||
|
self.ui.maintain(global_state.window.renderer_mut());
|
||||||
|
|
||||||
|
// Draw the UI to the screen
|
||||||
|
self.ui.render(global_state.window.renderer_mut());
|
||||||
|
|
||||||
// Finish the frame
|
// Finish the frame
|
||||||
global_state.window.renderer_mut().flush();
|
global_state.window.renderer_mut().flush();
|
||||||
global_state.window
|
global_state.window
|
||||||
|
@ -3,6 +3,7 @@ pub mod mesh;
|
|||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod pipelines;
|
pub mod pipelines;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
pub mod texture;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
@ -10,6 +11,7 @@ pub use self::{
|
|||||||
consts::Consts,
|
consts::Consts,
|
||||||
mesh::{Mesh, Tri, Quad},
|
mesh::{Mesh, Tri, Quad},
|
||||||
model::Model,
|
model::Model,
|
||||||
|
texture::Texture,
|
||||||
renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
|
renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
|
||||||
pipelines::{
|
pipelines::{
|
||||||
Globals,
|
Globals,
|
||||||
@ -27,6 +29,11 @@ pub use self::{
|
|||||||
TerrainPipeline,
|
TerrainPipeline,
|
||||||
Locals as TerrainLocals,
|
Locals as TerrainLocals,
|
||||||
},
|
},
|
||||||
|
ui::{
|
||||||
|
create_quad_mesh as create_ui_quad_mesh,
|
||||||
|
UiPipeline,
|
||||||
|
Locals as UiLocals,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,6 +48,7 @@ use gfx;
|
|||||||
pub enum RenderError {
|
pub enum RenderError {
|
||||||
PipelineError(gfx::PipelineStateError<String>),
|
PipelineError(gfx::PipelineStateError<String>),
|
||||||
UpdateError(gfx::UpdateError<usize>),
|
UpdateError(gfx::UpdateError<usize>),
|
||||||
|
CombinedError(gfx::CombinedError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to represent a specific rendering configuration.
|
/// Used to represent a specific rendering configuration.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
pub mod figure;
|
pub mod figure;
|
||||||
pub mod skybox;
|
pub mod skybox;
|
||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
|
pub mod ui;
|
||||||
|
|
||||||
// Library
|
// Library
|
||||||
use gfx::{
|
use gfx::{
|
||||||
|
76
voxygen/src/render/pipelines/ui.rs
Normal file
76
voxygen/src/render/pipelines/ui.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Library
|
||||||
|
use gfx::{
|
||||||
|
self,
|
||||||
|
// Macros
|
||||||
|
gfx_defines,
|
||||||
|
gfx_vertex_struct_meta,
|
||||||
|
gfx_constant_struct_meta,
|
||||||
|
gfx_impl_struct_meta,
|
||||||
|
gfx_pipeline,
|
||||||
|
gfx_pipeline_inner,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Local
|
||||||
|
use super::{
|
||||||
|
Globals,
|
||||||
|
super::{
|
||||||
|
Pipeline,
|
||||||
|
TgtColorFmt,
|
||||||
|
TgtDepthFmt,
|
||||||
|
Mesh,
|
||||||
|
Quad,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
gfx_defines! {
|
||||||
|
vertex Vertex {
|
||||||
|
pos: [f32; 3] = "v_pos",
|
||||||
|
uv: [f32; 2] = "v_uv",
|
||||||
|
}
|
||||||
|
|
||||||
|
constant Locals {
|
||||||
|
bounds: [f32; 4] = "bounds",
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline pipe {
|
||||||
|
vbuf: gfx::VertexBuffer<Vertex> = (),
|
||||||
|
|
||||||
|
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||||
|
tex: gfx::TextureSampler<[f32; 4]> = "u_tex",
|
||||||
|
|
||||||
|
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
|
||||||
|
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::PASS_TEST,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Locals {
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self { bounds: [0.0, 0.0, 1.0, 1.0] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(bounds: [f32; 4]) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UiPipeline;
|
||||||
|
|
||||||
|
impl Pipeline for UiPipeline {
|
||||||
|
type Vertex = Vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_quad_mesh() -> Mesh<UiPipeline> {
|
||||||
|
let mut mesh = Mesh::new();
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
mesh.push_quad(Quad::new(
|
||||||
|
Vertex { pos: [0.0, 0.0, 0.0], uv: [0.0, 0.0] },
|
||||||
|
Vertex { pos: [0.0, 1.0, 0.0], uv: [0.0, 1.0] },
|
||||||
|
Vertex { pos: [1.0, 1.0, 0.0], uv: [1.0, 1.0] },
|
||||||
|
Vertex { pos: [1.0, 0.0, 0.0], uv: [1.0, 0.0] },
|
||||||
|
));
|
||||||
|
|
||||||
|
mesh
|
||||||
|
}
|
@ -4,12 +4,14 @@ use gfx::{
|
|||||||
self,
|
self,
|
||||||
traits::{Device, FactoryExt},
|
traits::{Device, FactoryExt},
|
||||||
};
|
};
|
||||||
|
use image;
|
||||||
|
|
||||||
// Local
|
// Local
|
||||||
use super::{
|
use super::{
|
||||||
consts::Consts,
|
consts::Consts,
|
||||||
model::Model,
|
|
||||||
mesh::Mesh,
|
mesh::Mesh,
|
||||||
|
model::Model,
|
||||||
|
texture::Texture,
|
||||||
Pipeline,
|
Pipeline,
|
||||||
RenderError,
|
RenderError,
|
||||||
gfx_backend,
|
gfx_backend,
|
||||||
@ -18,6 +20,7 @@ use super::{
|
|||||||
figure,
|
figure,
|
||||||
skybox,
|
skybox,
|
||||||
terrain,
|
terrain,
|
||||||
|
ui,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,6 +48,7 @@ pub struct Renderer {
|
|||||||
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
|
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
|
||||||
figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
|
figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
|
||||||
terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>,
|
terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>,
|
||||||
|
ui_pipeline: GfxPipeline<ui::pipe::Init<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
@ -80,6 +84,14 @@ impl Renderer {
|
|||||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/terrain.frag")),
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/terrain.frag")),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Construct a pipeline for rendering UI elements
|
||||||
|
let ui_pipeline = create_pipeline(
|
||||||
|
&mut factory,
|
||||||
|
ui::pipe::new(),
|
||||||
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/ui.vert")),
|
||||||
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/ui.frag")),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
device,
|
device,
|
||||||
encoder: factory.create_command_buffer().into(),
|
encoder: factory.create_command_buffer().into(),
|
||||||
@ -91,6 +103,7 @@ impl Renderer {
|
|||||||
skybox_pipeline,
|
skybox_pipeline,
|
||||||
figure_pipeline,
|
figure_pipeline,
|
||||||
terrain_pipeline,
|
terrain_pipeline,
|
||||||
|
ui_pipeline,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +117,14 @@ impl Renderer {
|
|||||||
(&mut self.tgt_color_view, &mut self.tgt_depth_view)
|
(&mut self.tgt_color_view, &mut self.tgt_depth_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the resolution of the render target.
|
||||||
|
pub fn get_resolution(&self) -> Vec2<u16> {
|
||||||
|
Vec2::new(
|
||||||
|
self.tgt_color_view.get_dimensions().0,
|
||||||
|
self.tgt_color_view.get_dimensions().1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Queue the clearing of the color and depth targets ready for a new frame to be rendered.
|
/// Queue the clearing of the color and depth targets ready for a new frame to be rendered.
|
||||||
/// TODO: Make a version of this that doesn't clear the colour target for speed
|
/// TODO: Make a version of this that doesn't clear the colour target for speed
|
||||||
pub fn clear(&mut self, col: Rgba<f32>) {
|
pub fn clear(&mut self, col: Rgba<f32>) {
|
||||||
@ -144,6 +165,14 @@ impl Renderer {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Queue the rendering of the provided skybox model in the upcoming frame.
|
/// Queue the rendering of the provided skybox model in the upcoming frame.
|
||||||
pub fn render_skybox(
|
pub fn render_skybox(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -205,6 +234,26 @@ impl Renderer {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Queue the rendering of the provided UI element in the upcoming frame.
|
||||||
|
pub fn render_ui_element(
|
||||||
|
&mut self,
|
||||||
|
model: &Model<ui::UiPipeline>,
|
||||||
|
locals: &Consts<ui::Locals>,
|
||||||
|
tex: &Texture<ui::UiPipeline>,
|
||||||
|
) {
|
||||||
|
self.encoder.draw(
|
||||||
|
&model.slice,
|
||||||
|
&self.ui_pipeline.pso,
|
||||||
|
&ui::pipe::Data {
|
||||||
|
vbuf: model.vbuf.clone(),
|
||||||
|
locals: locals.buf.clone(),
|
||||||
|
tex: (tex.srv.clone(), tex.sampler.clone()),
|
||||||
|
tgt_color: self.tgt_color_view.clone(),
|
||||||
|
tgt_depth: self.tgt_depth_view.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GfxPipeline<P: gfx::pso::PipelineInit> {
|
struct GfxPipeline<P: gfx::pso::PipelineInit> {
|
||||||
|
58
voxygen/src/render/texture.rs
Normal file
58
voxygen/src/render/texture.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Standard
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
// Library
|
||||||
|
use gfx::{
|
||||||
|
self,
|
||||||
|
traits::{Factory, FactoryExt},
|
||||||
|
};
|
||||||
|
use image::{
|
||||||
|
DynamicImage,
|
||||||
|
GenericImageView,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Local
|
||||||
|
use super::{
|
||||||
|
RenderError,
|
||||||
|
mesh::Mesh,
|
||||||
|
Pipeline,
|
||||||
|
gfx_backend,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 sampler: gfx::handle::Sampler<gfx_backend::Resources>,
|
||||||
|
_phantom: PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Pipeline> Texture<P> {
|
||||||
|
pub fn new(
|
||||||
|
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()],
|
||||||
|
)
|
||||||
|
.map_err(|err| RenderError::CombinedError(err))?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
tex,
|
||||||
|
srv,
|
||||||
|
sampler: factory.create_sampler(gfx::texture::SamplerInfo::new(
|
||||||
|
gfx::texture::FilterMethod::Scale,
|
||||||
|
gfx::texture::WrapMode::Clamp,
|
||||||
|
)),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,12 @@ impl Camera {
|
|||||||
.max(-PI / 2.0);
|
.max(-PI / 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Zoom the camera by the given delta, limiting the input accordingly.
|
||||||
|
pub fn zoom_by(&mut self, delta: f32) {
|
||||||
|
// Clamp camera dist to the 0 <= x <= infinity range
|
||||||
|
self.dist = (self.dist + delta).max(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the focus position of the 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.
|
/// Set the focus position of the camera.
|
||||||
|
@ -112,13 +112,25 @@ impl Scene {
|
|||||||
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.).
|
/// Handle an incoming user input event (i.e: cursor moved, key pressed, window closed, etc.).
|
||||||
|
///
|
||||||
|
/// If the event is handled, return true
|
||||||
pub fn handle_input_event(&mut self, event: Event) -> bool {
|
pub fn handle_input_event(&mut self, event: Event) -> bool {
|
||||||
match event {
|
match event {
|
||||||
|
// When the window is resized, change the camera's aspect ratio
|
||||||
|
Event::Resize(dims) => {
|
||||||
|
self.camera.set_aspect_ratio(dims.x as f32 / dims.y as f32);
|
||||||
|
true
|
||||||
|
},
|
||||||
// Panning the cursor makes the camera rotate
|
// Panning the cursor makes the camera rotate
|
||||||
Event::CursorPan(delta) => {
|
Event::CursorPan(delta) => {
|
||||||
self.camera.rotate_by(Vec3::from(delta) * CURSOR_PAN_SCALE);
|
self.camera.rotate_by(Vec3::from(delta) * CURSOR_PAN_SCALE);
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|
// Zoom the camera when a zoom event occurs
|
||||||
|
Event::Zoom(delta) => {
|
||||||
|
self.camera.zoom_by(delta);
|
||||||
|
true
|
||||||
|
},
|
||||||
// All other events are unhandled
|
// All other events are unhandled
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -95,10 +95,6 @@ impl PlayState for SessionState {
|
|||||||
for event in global_state.window.fetch_events() {
|
for event in global_state.window.fetch_events() {
|
||||||
let _handled = match event {
|
let _handled = match event {
|
||||||
Event::Close => return PlayStateResult::Shutdown,
|
Event::Close => return PlayStateResult::Shutdown,
|
||||||
// When the window is resized, change the camera's aspect ratio
|
|
||||||
Event::Resize(dims) => {
|
|
||||||
self.scene.camera_mut().set_aspect_ratio(dims.x as f32 / dims.y as f32);
|
|
||||||
},
|
|
||||||
// When 'q' is pressed, exit the session
|
// When 'q' is pressed, exit the session
|
||||||
Event::Char('q') => return PlayStateResult::Pop,
|
Event::Char('q') => return PlayStateResult::Pop,
|
||||||
// Toggle cursor grabbing
|
// Toggle cursor grabbing
|
||||||
|
80
voxygen/src/ui/element/image.rs
Normal file
80
voxygen/src/ui/element/image.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Standard
|
||||||
|
use std::{
|
||||||
|
rc::Rc,
|
||||||
|
cell::RefCell,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Library
|
||||||
|
use image::DynamicImage;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
// Crate
|
||||||
|
use crate::render::{
|
||||||
|
Consts,
|
||||||
|
UiLocals,
|
||||||
|
Renderer,
|
||||||
|
Texture,
|
||||||
|
UiPipeline,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Local
|
||||||
|
use super::{
|
||||||
|
super::{
|
||||||
|
UiError,
|
||||||
|
Cache,
|
||||||
|
},
|
||||||
|
Element,
|
||||||
|
Bounds,
|
||||||
|
SizeRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Image {
|
||||||
|
texture: Rc<Texture<UiPipeline>>,
|
||||||
|
locals: Consts<UiLocals>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
pub fn new(renderer: &mut Renderer, image: &DynamicImage) -> Result<Self, UiError> {
|
||||||
|
Ok(Self {
|
||||||
|
texture: Rc::new(
|
||||||
|
renderer.create_texture(image)
|
||||||
|
.map_err(|err| UiError::RenderError(err))?
|
||||||
|
),
|
||||||
|
locals: renderer.create_consts(&[UiLocals::default()])
|
||||||
|
.map_err(|err| UiError::RenderError(err))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element for Image {
|
||||||
|
fn get_hsize_request(&self) -> SizeRequest { SizeRequest::indifferent() }
|
||||||
|
fn get_vsize_request(&self) -> SizeRequest { SizeRequest::indifferent() }
|
||||||
|
|
||||||
|
fn maintain(
|
||||||
|
&mut self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
cache: &Cache,
|
||||||
|
bounds: Bounds<f32>,
|
||||||
|
resolution: Vec2<f32>,
|
||||||
|
) {
|
||||||
|
renderer.update_consts(&mut self.locals, &[UiLocals::new(
|
||||||
|
[bounds.x, bounds.y, bounds.w, bounds.h],
|
||||||
|
)])
|
||||||
|
.expect("Could not update UI image consts");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
cache: &Cache,
|
||||||
|
bounds: Bounds<f32>,
|
||||||
|
resolution: Vec2<f32>,
|
||||||
|
) {
|
||||||
|
renderer.render_ui_element(
|
||||||
|
cache.model(),
|
||||||
|
&self.locals,
|
||||||
|
&self.texture,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
181
voxygen/src/ui/element/mod.rs
Normal file
181
voxygen/src/ui/element/mod.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
pub mod image;
|
||||||
|
|
||||||
|
// Standard
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
// Library
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
// Crate
|
||||||
|
use crate::render::{
|
||||||
|
Renderer,
|
||||||
|
Texture,
|
||||||
|
Consts,
|
||||||
|
UiLocals,
|
||||||
|
UiPipeline,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Local
|
||||||
|
use super::{
|
||||||
|
UiError,
|
||||||
|
Cache,
|
||||||
|
Span,
|
||||||
|
SizeRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bounds
|
||||||
|
|
||||||
|
pub type Bounds<T> = Rect<T, T>;
|
||||||
|
|
||||||
|
pub trait BoundsExt {
|
||||||
|
fn relative_to(self, other: Self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundsExt for Bounds<f32> {
|
||||||
|
fn relative_to(self, other: Self) -> Self {
|
||||||
|
Self::new(
|
||||||
|
other.x + self.x * other.w,
|
||||||
|
other.y + self.y * other.h,
|
||||||
|
self.w * other.w,
|
||||||
|
self.h * other.h,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BoundsSpan {
|
||||||
|
fn in_resolution(self, resolution: Vec2<f32>) -> Bounds<f32>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundsSpan for Bounds<Span> {
|
||||||
|
fn in_resolution(self, resolution: Vec2<f32>) -> Bounds<f32> {
|
||||||
|
Bounds::new(
|
||||||
|
self.x.to_rel(resolution.x).rel,
|
||||||
|
self.y.to_rel(resolution.y).rel,
|
||||||
|
self.w.to_rel(resolution.x).rel,
|
||||||
|
self.h.to_rel(resolution.y).rel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element
|
||||||
|
|
||||||
|
pub trait Element: 'static {
|
||||||
|
//fn deep_clone(&self) -> Rc<dyn Element>;
|
||||||
|
|
||||||
|
fn get_hsize_request(&self) -> SizeRequest;
|
||||||
|
fn get_vsize_request(&self) -> SizeRequest;
|
||||||
|
|
||||||
|
fn maintain(
|
||||||
|
&mut self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
cache: &Cache,
|
||||||
|
bounds: Bounds<f32>,
|
||||||
|
resolution: Vec2<f32>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
cache: &Cache,
|
||||||
|
bounds: Bounds<f32>,
|
||||||
|
resolution: Vec2<f32>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surface
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Surface {
|
||||||
|
Transparent,
|
||||||
|
Color(Rgba<f32>),
|
||||||
|
Texture(Rc<Texture<UiPipeline>>),
|
||||||
|
Bevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Widget<E: Element> {
|
||||||
|
inner: Box<E>,
|
||||||
|
background: Surface,
|
||||||
|
margin_top: Span,
|
||||||
|
margin_bottom: Span,
|
||||||
|
margin_left: Span,
|
||||||
|
margin_right: Span,
|
||||||
|
locals: Consts<UiLocals>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Element> Widget<E> {
|
||||||
|
pub fn new(renderer: &mut Renderer, inner: E) -> Result<Box<Self>, UiError> {
|
||||||
|
Ok(Box::new(Self {
|
||||||
|
inner: Box::new(inner),
|
||||||
|
background: Surface::Transparent,
|
||||||
|
margin_top: Span::rel(0.2),
|
||||||
|
margin_bottom: Span::rel(0.2),
|
||||||
|
margin_left: Span::rel(0.2),
|
||||||
|
margin_right: Span::rel(0.2),
|
||||||
|
locals: renderer.create_consts(&[UiLocals::default()])
|
||||||
|
.map_err(|err| UiError::RenderError(err))?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_inner_bounds(&self) -> Bounds<Span> {
|
||||||
|
Bounds::new(
|
||||||
|
self.margin_left,
|
||||||
|
self.margin_top,
|
||||||
|
Span::full() - self.margin_left - self.margin_right,
|
||||||
|
Span::full() - self.margin_top - self.margin_bottom,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Element> Element for Widget<E> {
|
||||||
|
fn get_hsize_request(&self) -> SizeRequest {
|
||||||
|
self.inner.get_hsize_request() + self.margin_left + self.margin_right
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vsize_request(&self) -> SizeRequest {
|
||||||
|
self.inner.get_vsize_request() + self.margin_top + self.margin_bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maintain(
|
||||||
|
&mut self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
cache: &Cache,
|
||||||
|
bounds: Bounds<f32>,
|
||||||
|
resolution: Vec2<f32>,
|
||||||
|
) {
|
||||||
|
renderer.update_consts(&mut self.locals, &[UiLocals::new(
|
||||||
|
[bounds.x, bounds.y, bounds.w, bounds.h],
|
||||||
|
)])
|
||||||
|
.expect("Could not update UI image consts");
|
||||||
|
|
||||||
|
let inner_bounds = self
|
||||||
|
.get_inner_bounds()
|
||||||
|
.in_resolution(resolution)
|
||||||
|
.relative_to(bounds);
|
||||||
|
|
||||||
|
self.inner.maintain(renderer, cache, inner_bounds, resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&self,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
cache: &Cache,
|
||||||
|
bounds: Bounds<f32>,
|
||||||
|
resolution: Vec2<f32>,
|
||||||
|
) {
|
||||||
|
renderer.render_ui_element(
|
||||||
|
cache.model(),
|
||||||
|
&self.locals,
|
||||||
|
&cache.blank_texture(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let inner_bounds = self
|
||||||
|
.get_inner_bounds()
|
||||||
|
.in_resolution(resolution)
|
||||||
|
.relative_to(bounds);
|
||||||
|
|
||||||
|
self.inner.render(renderer, cache, inner_bounds, resolution);
|
||||||
|
}
|
||||||
|
}
|
85
voxygen/src/ui/mod.rs
Normal file
85
voxygen/src/ui/mod.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
pub mod element;
|
||||||
|
pub mod size_request;
|
||||||
|
pub mod span;
|
||||||
|
|
||||||
|
// Reexports
|
||||||
|
pub use self::{
|
||||||
|
span::Span,
|
||||||
|
size_request::SizeRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Library
|
||||||
|
use image::DynamicImage;
|
||||||
|
|
||||||
|
// Crate
|
||||||
|
use crate::{
|
||||||
|
Error,
|
||||||
|
render::{
|
||||||
|
RenderError,
|
||||||
|
Renderer,
|
||||||
|
Model,
|
||||||
|
Texture,
|
||||||
|
UiPipeline,
|
||||||
|
create_ui_quad_mesh,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Local
|
||||||
|
use self::element::{
|
||||||
|
Element,
|
||||||
|
Bounds,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UiError {
|
||||||
|
RenderError(RenderError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cache {
|
||||||
|
model: Model<UiPipeline>,
|
||||||
|
blank_texture: Texture<UiPipeline>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cache {
|
||||||
|
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
model: renderer.create_model(&create_ui_quad_mesh())?,
|
||||||
|
blank_texture: renderer.create_texture(&DynamicImage::new_rgba8(1, 1))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn model(&self) -> &Model<UiPipeline> { &self.model }
|
||||||
|
pub fn blank_texture(&self) -> &Texture<UiPipeline> { &self.blank_texture }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ui {
|
||||||
|
base: Box<dyn Element>,
|
||||||
|
cache: Cache,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ui {
|
||||||
|
pub fn new<E: Element>(renderer: &mut Renderer, base: Box<E>) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
base,
|
||||||
|
cache: Cache::new(renderer)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||||
|
self.base.maintain(
|
||||||
|
renderer,
|
||||||
|
&self.cache,
|
||||||
|
Bounds::new(0.0, 0.0, 1.0, 1.0),
|
||||||
|
renderer.get_resolution().map(|e| e as f32),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, renderer: &mut Renderer) {
|
||||||
|
self.base.render(
|
||||||
|
renderer,
|
||||||
|
&self.cache,
|
||||||
|
Bounds::new(0.0, 0.0, 1.0, 1.0),
|
||||||
|
renderer.get_resolution().map(|e| e as f32),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
30
voxygen/src/ui/size_request.rs
Normal file
30
voxygen/src/ui/size_request.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Standard
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
|
// Local
|
||||||
|
use super::Span;
|
||||||
|
|
||||||
|
pub struct SizeRequest {
|
||||||
|
min: Span,
|
||||||
|
max: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeRequest {
|
||||||
|
pub fn indifferent() -> Self {
|
||||||
|
Self {
|
||||||
|
min: Span::rel(0.0),
|
||||||
|
max: Span::rel(std::f32::INFINITY),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Span> for SizeRequest {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, span: Span) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min + span,
|
||||||
|
max: self.max + span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
voxygen/src/ui/span.rs
Normal file
47
voxygen/src/ui/span.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Standard
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Span {
|
||||||
|
pub rel: f32,
|
||||||
|
pub abs: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Span {
|
||||||
|
pub fn rel(rel: f32) -> Self { Self { rel, abs: 0.0 } }
|
||||||
|
pub fn abs(abs: f32) -> Self { Self { rel: 0.0, abs } }
|
||||||
|
|
||||||
|
pub fn full() -> Self { Self { rel: 1.0, abs: 0.0 } }
|
||||||
|
pub fn half() -> Self { Self { rel: 0.5, abs: 0.0 } }
|
||||||
|
pub fn none() -> Self { Self { rel: 0.0, abs: 0.0 } }
|
||||||
|
|
||||||
|
pub fn to_abs(self, res: f32) -> Self {
|
||||||
|
Self { rel: 0.0, abs: self.rel * res + self.abs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_rel(self, res: f32) -> Self {
|
||||||
|
Self { rel: self.rel + self.abs / res, abs: 0.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Span {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
rel: self.rel + other.rel,
|
||||||
|
abs: self.abs + other.abs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Span {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
rel: self.rel - other.rel,
|
||||||
|
abs: self.abs - other.abs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -97,6 +97,10 @@ impl Window {
|
|||||||
glutin::Event::DeviceEvent { event, .. } => match event {
|
glutin::Event::DeviceEvent { event, .. } => match event {
|
||||||
glutin::DeviceEvent::MouseMotion { delta: (dx, dy), .. } if cursor_grabbed =>
|
glutin::DeviceEvent::MouseMotion { delta: (dx, dy), .. } if cursor_grabbed =>
|
||||||
events.push(Event::CursorPan(Vec2::new(dx as f32, dy as f32))),
|
events.push(Event::CursorPan(Vec2::new(dx as f32, dy as f32))),
|
||||||
|
glutin::DeviceEvent::MouseWheel {
|
||||||
|
delta: glutin::MouseScrollDelta::LineDelta(_x, y),
|
||||||
|
..
|
||||||
|
} if cursor_grabbed => events.push(Event::Zoom(y as f32)),
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@ -136,6 +140,8 @@ pub enum Event {
|
|||||||
Char(char),
|
Char(char),
|
||||||
/// The cursor has been panned across the screen while grabbed.
|
/// The cursor has been panned across the screen while grabbed.
|
||||||
CursorPan(Vec2<f32>),
|
CursorPan(Vec2<f32>),
|
||||||
|
/// The camera has been requested to zoom.
|
||||||
|
Zoom(f32),
|
||||||
/// A key that the game recognises has been pressed down
|
/// A key that the game recognises has been pressed down
|
||||||
KeyDown(Key),
|
KeyDown(Key),
|
||||||
/// A key that the game recognises has been released down
|
/// A key that the game recognises has been released down
|
||||||
|
1
voxygen/test_assets/.gitattributes
vendored
Normal file
1
voxygen/test_assets/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
BIN
voxygen/test_assets/test.png
(Stored with Git LFS)
Normal file
BIN
voxygen/test_assets/test.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
voxygen/test_assets/wall.png
(Stored with Git LFS)
Normal file
BIN
voxygen/test_assets/wall.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -32,6 +32,7 @@ impl World {
|
|||||||
let air = Block::empty();
|
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(50, 255, 0));
|
let grass = Block::new(2, Rgb::new(50, 255, 0));
|
||||||
|
let sand = Block::new(3, Rgb::new(180, 150, 50));
|
||||||
|
|
||||||
let perlin_nz = Perlin::new();
|
let perlin_nz = Perlin::new();
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ impl World {
|
|||||||
if wposf.z < height - 1.0 {
|
if wposf.z < height - 1.0 {
|
||||||
stone
|
stone
|
||||||
} else {
|
} else {
|
||||||
grass
|
sand
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
air
|
air
|
||||||
|
Loading…
Reference in New Issue
Block a user