Added figures, segments, test .vox files, basic animation test

This commit is contained in:
Joshua Barretto 2019-01-13 20:53:55 +00:00
parent b37a6b67f9
commit 8184bc6fc0
37 changed files with 790 additions and 100 deletions

View File

@ -7,3 +7,4 @@ edition = "2018"
[dependencies] [dependencies]
specs = "0.14" specs = "0.14"
vek = "0.9" vek = "0.9"
dot_vox = "1.0"

View File

@ -1,7 +1,7 @@
pub mod phys; pub mod phys;
// External // External
use specs::{World as EcsWorld, Builder}; use specs::World as EcsWorld;
pub fn register_local_components(ecs_world: &mut EcsWorld) { pub fn register_local_components(ecs_world: &mut EcsWorld) {
ecs_world.register::<phys::Pos>(); ecs_world.register::<phys::Pos>();

33
common/src/figure/cell.rs Normal file
View File

@ -0,0 +1,33 @@
// Library
use vek::*;
/// A type representing a single voxel in a figure
#[derive(Copy, Clone, Debug)]
pub enum Cell {
Filled([u8; 3]),
Empty,
}
impl Cell {
pub fn empty() -> Self {
Cell::Empty
}
pub fn new(rgb: Rgb<u8>) -> Self {
Cell::Filled(rgb.into_array())
}
pub fn is_empty(&self) -> bool {
match self {
Cell::Filled(_) => false,
Cell::Empty => true,
}
}
pub fn get_color(&self) -> Option<Rgb<u8>> {
match self {
Cell::Filled(col) => Some(Rgb::from(*col)),
Cell::Empty => None,
}
}
}

63
common/src/figure/mod.rs Normal file
View File

@ -0,0 +1,63 @@
pub mod cell;
// Library
use vek::*;
use dot_vox::DotVoxData;
// Crate
use crate::{
vol::WriteVol,
volumes::dyna::Dyna,
};
// Local
use self::cell::Cell;
/// A type representing a single figure bone (e.g: the limb of a character).
#[derive(Copy, Clone)]
pub struct Bone {
origin: Vec3<f32>,
offset: Vec3<f32>,
ori: Vec3<f32>,
}
/// A type representing a volume that may be part of an animated figure.
///
/// Figures are used to represent things like characters, NPCs, mobs, etc.
pub type Segment = Dyna<Cell, ()>;
impl From<DotVoxData> for Segment {
fn from(dot_vox_data: DotVoxData) -> Self {
if let Some(model) = dot_vox_data.models.get(0) {
let palette = dot_vox_data
.palette
.iter()
.map(|col| Rgba::from(col.to_ne_bytes()).into())
.collect::<Vec<_>>();
let mut segment = Segment::filled(
Vec3::new(
model.size.x,
model.size.y,
model.size.z,
),
Cell::empty(),
(),
);
for voxel in &model.voxels {
if let Some(&color) = palette.get(voxel.i as usize) {
// TODO: Maybe don't ignore this error?
let _ = segment.set(
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| e as i32),
Cell::new(color),
);
}
}
segment
} else {
Segment::filled(Vec3::zero(), Cell::empty(), ())
}
}
}

View File

@ -2,6 +2,7 @@
pub mod clock; pub mod clock;
pub mod comp; pub mod comp;
pub mod figure;
pub mod state; pub mod state;
pub mod terrain; pub mod terrain;
pub mod volumes; pub mod volumes;

View File

@ -8,7 +8,6 @@ use specs::World as EcsWorld;
use crate::{ use crate::{
comp, comp,
terrain::TerrainMap, terrain::TerrainMap,
vol::VolSize,
}; };
/// How much faster should an in-game day be compared to a real day? /// How much faster should an in-game day be compared to a real day?

View File

@ -1,3 +1,4 @@
#[derive(Copy, Clone, Debug)]
pub struct Block { pub struct Block {
kind: u8, kind: u8,
color: [u8; 3], color: [u8; 3],

View File

@ -1,27 +1,76 @@
// Library // Library
use vek::*; use vek::*;
/// A volume that contains voxel data.
pub trait BaseVol { pub trait BaseVol {
type Vox; type Vox;
type Err; type Err;
} }
pub trait SizedVol: BaseVol { // Utility types
const SIZE: Vec3<u32>;
pub struct VoxPosIter {
pos: Vec3<u32>,
sz: Vec3<u32>,
} }
impl Iterator for VoxPosIter {
type Item = Vec3<i32>;
fn next(&mut self) -> Option<Self::Item> {
let mut old_pos = self.pos;
if old_pos.z == self.sz.z {
old_pos.z = 0;
old_pos.y += 1;
if old_pos.y == self.sz.y {
old_pos.y = 0;
old_pos.x += 1;
if old_pos.x == self.sz.x {
return None;
}
}
}
self.pos = old_pos + Vec3::unit_z();
Some(old_pos.map(|e| e as i32))
}
}
/// A volume that has a finite size.
pub trait SizedVol: BaseVol {
/// Get the size of the volume.
#[inline(always)]
fn get_size(&self) -> Vec3<u32>;
/// Iterate through all potential voxel positions in this volume
fn iter_positions(&self) -> VoxPosIter {
VoxPosIter {
pos: Vec3::zero(),
sz: self.get_size(),
}
}
}
/// A volume that provided read access to its voxel data.
pub trait ReadVol: BaseVol { pub trait ReadVol: BaseVol {
/// Get a reference to the voxel at the provided position in the volume.
#[inline(always)] #[inline(always)]
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>; fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>;
} }
/// A volume that provides write access to its voxel data.
pub trait WriteVol: BaseVol { pub trait WriteVol: BaseVol {
/// Set the voxel at the provided position in the volume to the provided value.
#[inline(always)] #[inline(always)]
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Err>; fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Err>;
} }
// Utility traits // Utility traits
/// Used to specify a volume's compile-time size. This exists as a substitute until const generics
/// are implemented.
pub trait VolSize { pub trait VolSize {
const SIZE: Vec3<u32>; const SIZE: Vec3<u32>;
} }

View File

@ -17,6 +17,7 @@ pub enum ChunkErr {
OutOfBounds, OutOfBounds,
} }
/// A volume with dimensions known at compile-time.
// V = Voxel // V = Voxel
// S = Size (replace when const generics are a thing) // S = Size (replace when const generics are a thing)
// M = Metadata // M = Metadata
@ -27,6 +28,8 @@ pub struct Chunk<V, S: VolSize, M> {
} }
impl<V, S: VolSize, M> Chunk<V, S, M> { impl<V, S: VolSize, M> Chunk<V, S, M> {
/// Used to transform a voxel position in the volume into its corresponding index in the voxel
// array.
#[inline(always)] #[inline(always)]
fn idx_for(pos: Vec3<i32>) -> Option<usize> { fn idx_for(pos: Vec3<i32>) -> Option<usize> {
if if
@ -50,7 +53,8 @@ impl<V, S: VolSize, M> BaseVol for Chunk<V, S, M> {
} }
impl<V, S: VolSize, M> SizedVol for Chunk<V, S, M> { impl<V, S: VolSize, M> SizedVol for Chunk<V, S, M> {
const SIZE: Vec3<u32> = Vec3 { x: 32, y: 32, z: 32 }; #[inline(always)]
fn get_size(&self) -> Vec3<u32> { S::SIZE }
} }
impl<V, S: VolSize, M> ReadVol for Chunk<V, S, M> { impl<V, S: VolSize, M> ReadVol for Chunk<V, S, M> {
@ -73,6 +77,8 @@ impl<V, S: VolSize, M> WriteVol for Chunk<V, S, M> {
} }
impl<V: Clone, S: VolSize, M> Chunk<V, S, M> { impl<V: Clone, S: VolSize, M> Chunk<V, S, M> {
/// Create a new `Chunk` with the provided dimensions and all voxels filled with duplicates of
/// the provided voxel.
pub fn filled(vox: V, meta: M) -> Self { pub fn filled(vox: V, meta: M) -> Self {
Self { Self {
vox: vec![vox; S::SIZE.product() as usize], vox: vec![vox; S::SIZE.product() as usize],
@ -81,10 +87,12 @@ impl<V: Clone, S: VolSize, M> Chunk<V, S, M> {
} }
} }
/// Get a reference to the internal metadata.
pub fn metadata(&self) -> &M { pub fn metadata(&self) -> &M {
&self.meta &self.meta
} }
/// Get a mutable reference to the internal metadata.
pub fn metadata_mut(&mut self) -> &mut M { pub fn metadata_mut(&mut self) -> &mut M {
&mut self.meta &mut self.meta
} }

View File

@ -0,0 +1,95 @@
// Library
use vek::*;
// Local
use crate::vol::{
BaseVol,
SizedVol,
ReadVol,
WriteVol,
};
pub enum DynaErr {
OutOfBounds,
}
/// A volume with dimensions known only at the creation of the object.
// V = Voxel
// S = Size (replace when const generics are a thing)
// M = Metadata
pub struct Dyna<V, M> {
vox: Vec<V>,
meta: M,
sz: Vec3<u32>,
}
impl<V, M> Dyna<V, M> {
/// Used to transform a voxel position in the volume into its corresponding index in the voxel
// 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)
} else {
None
}
}
}
impl<V, M> BaseVol for Dyna<V, M> {
type Vox = V;
type Err = DynaErr;
}
impl<V, M> SizedVol for Dyna<V, M> {
#[inline(always)]
fn get_size(&self) -> Vec3<u32> { self.sz }
}
impl<V, M> ReadVol for Dyna<V, M> {
#[inline(always)]
fn get(&self, pos: Vec3<i32>) -> Result<&V, DynaErr> {
Self::idx_for(self.sz, pos)
.and_then(|idx| self.vox.get(idx))
.ok_or(DynaErr::OutOfBounds)
}
}
impl<V, M> WriteVol for Dyna<V, M> {
#[inline(always)]
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), DynaErr> {
Self::idx_for(self.sz, pos)
.and_then(|idx| self.vox.get_mut(idx))
.map(|old_vox| *old_vox = vox)
.ok_or(DynaErr::OutOfBounds)
}
}
impl<V: Clone, M> Dyna<V, M> {
/// Create a new `Dyna` with the provided dimensions and all voxels filled with duplicates of
/// the provided voxel.
pub fn filled(sz: Vec3<u32>, vox: V, meta: M) -> Self {
Self {
vox: vec![vox; sz.product() as usize],
meta,
sz,
}
}
/// Get a reference to the internal metadata.
pub fn metadata(&self) -> &M {
&self.meta
}
/// Get a mutable reference to the internal metadata.
pub fn metadata_mut(&mut self) -> &mut M {
&mut self.meta
}
}

View File

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

View File

@ -7,7 +7,7 @@ edition = "2018"
[features] [features]
gl = ["gfx_device_gl"] gl = ["gfx_device_gl"]
default = [] default = ["gl"]
[dependencies] [dependencies]
common = { package = "veloren-common", path = "../common" } common = { package = "veloren-common", path = "../common" }
@ -28,3 +28,4 @@ failure = "0.1"
lazy_static = "1.1" lazy_static = "1.1"
log = "0.4" log = "0.4"
pretty_env_logger = "0.3" pretty_env_logger = "0.3"
dot_vox = "1.0"

View File

@ -1,6 +1,7 @@
#version 330 core #version 330 core
in vec3 f_pos; in vec3 f_pos;
in vec3 f_col;
layout (std140) layout (std140)
uniform u_locals { uniform u_locals {
@ -21,5 +22,5 @@ uniform u_globals {
out vec4 tgt_color; out vec4 tgt_color;
void main() { void main() {
tgt_color = vec4(f_pos, 1.0); tgt_color = vec4(f_col, 1.0);
} }

View File

@ -2,7 +2,7 @@
in vec3 v_pos; in vec3 v_pos;
in vec3 v_col; in vec3 v_col;
in uint v_bone; in uint v_bone_idx;
layout (std140) layout (std140)
uniform u_locals { uniform u_locals {
@ -20,13 +20,26 @@ uniform u_globals {
vec4 time; vec4 time;
}; };
struct BoneData {
mat4 bone_mat;
};
layout (std140)
uniform u_bones {
BoneData bones[16];
};
out vec3 f_pos; out vec3 f_pos;
out vec3 f_col;
void main() { void main() {
f_pos = v_pos; f_pos = v_pos;
f_col = v_col;
gl_Position = gl_Position =
proj_mat * proj_mat *
view_mat * view_mat *
vec4(0.5 * v_pos + cam_pos.xyz, 1); model_mat *
bones[v_bone_idx].bone_mat *
vec4(v_pos, 1);
} }

63
voxygen/src/anim/mod.rs Normal file
View File

@ -0,0 +1,63 @@
// Library
use vek::*;
// Crate
use crate::render::FigureBoneData;
#[derive(Copy, Clone)]
pub struct Bone {
parent: Option<u8>, // MUST be less than the current bone index
pub offset: Vec3<f32>,
pub ori: Quaternion<f32>,
}
impl Bone {
pub fn default() -> Self {
Self {
parent: None,
offset: Vec3::zero(),
ori: Quaternion::identity(),
}
}
pub fn compute_base_matrix(&self) -> Mat4<f32> {
Mat4::<f32>::translation_3d(self.offset) * Mat4::from(self.ori)
}
}
#[derive(Copy, Clone)]
pub struct Skeleton {
bones: [Bone; 16],
}
impl Skeleton {
pub fn default() -> Self {
Self {
bones: [Bone::default(); 16],
}
}
pub fn with_bone(mut self, bone_idx: u8, bone: Bone) -> Self {
self.bones[bone_idx as usize] = bone;
self
}
pub fn bone(&self, bone_idx: u8) -> &Bone { &self.bones[bone_idx as usize] }
pub fn bone_mut(&mut self, bone_idx: u8) -> &mut Bone { &mut self.bones[bone_idx as usize] }
pub fn compute_matrices(&self) -> [FigureBoneData; 16] {
let mut bone_data = [FigureBoneData::default(); 16];
for i in 0..16 {
bone_data[i] = FigureBoneData::new(
self.bones[i].compute_base_matrix()
// *
//if let Some(parent_idx) = self.bones[i].parent {
// bone_data[parent_idx as usize]
//} else {
// Mat4::identity()
//}
);
}
bone_data
}
}

View File

@ -1,5 +1,7 @@
pub mod anim;
pub mod error; pub mod error;
pub mod menu; pub mod menu;
pub mod mesh;
pub mod render; pub mod render;
pub mod scene; pub mod scene;
pub mod session; pub mod session;

20
voxygen/src/mesh/mod.rs Normal file
View File

@ -0,0 +1,20 @@
pub mod segment;
// Library
use vek::*;
// Crate
use crate::render::{
self,
Mesh,
};
pub trait Meshable {
type Pipeline: render::Pipeline;
fn generate_mesh(&self) -> Mesh<Self::Pipeline> {
self.generate_mesh_with_offset(Vec3::zero())
}
fn generate_mesh_with_offset(&self, offs: Vec3<f32>) -> Mesh<Self::Pipeline>;
}

112
voxygen/src/mesh/segment.rs Normal file
View File

@ -0,0 +1,112 @@
// Project
use common::figure::Segment;
// Library
use vek::*;
// Project
use common::vol::{
SizedVol,
ReadVol,
};
// Crate
use crate::{
mesh::Meshable,
render::{
self,
Mesh,
Quad,
FigurePipeline,
},
};
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>,
col: Rgb<f32>,
bone: u8,
) -> Quad<FigurePipeline> {
Quad::new(
FigureVertex::new(origin, col, bone),
FigureVertex::new(origin + unit_x, col, bone),
FigureVertex::new(origin + unit_x + unit_y, col, bone),
FigureVertex::new(origin + unit_y, col, bone),
)
}
impl Meshable for Segment {
type Pipeline = FigurePipeline;
fn generate_mesh_with_offset(&self, offs: Vec3<f32>) -> Mesh<FigurePipeline> {
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())
{
let col = col.map(|e| e as f32 / 255.0);
// TODO: Face occlusion
// -x
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_y(),
-Vec3::unit_y(),
Vec3::unit_z(),
col,
0,
));
// +x
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_x(),
Vec3::unit_y(),
Vec3::unit_z(),
col,
0,
));
// -y
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32),
Vec3::unit_x(),
Vec3::unit_z(),
col,
0,
));
// +y
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_y(),
Vec3::unit_z(),
Vec3::unit_x(),
col,
0,
));
// -z
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32),
Vec3::unit_y(),
Vec3::unit_x(),
col,
0,
));
// +z
mesh.push_quad(create_quad(
offs + pos.map(|e| e as f32) + Vec3::unit_z(),
Vec3::unit_x(),
Vec3::unit_y(),
col,
0,
));
}
}
mesh
}
}

View File

@ -19,9 +19,9 @@ pub struct Consts<T: Copy + gfx::traits::Pod> {
impl<T: Copy + gfx::traits::Pod> Consts<T> { impl<T: Copy + gfx::traits::Pod> Consts<T> {
/// Create a new `Const<T>` /// Create a new `Const<T>`
pub fn new(factory: &mut gfx_backend::Factory) -> Self { pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Self {
Self { Self {
buf: factory.create_constant_buffer(1), buf: factory.create_constant_buffer(len),
} }
} }
@ -29,9 +29,9 @@ impl<T: Copy + gfx::traits::Pod> Consts<T> {
pub fn update( pub fn update(
&mut self, &mut self,
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>, encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
val: T, vals: &[T],
) -> Result<(), RenderError> { ) -> Result<(), RenderError> {
encoder.update_buffer(&self.buf, &[val], 0) encoder.update_buffer(&self.buf, vals, 0)
.map_err(|err| RenderError::UpdateError(err)) .map_err(|err| RenderError::UpdateError(err))
} }
} }

View File

@ -2,6 +2,7 @@
use super::Pipeline; use super::Pipeline;
/// A `Vec`-based mesh structure used to store mesh data on the CPU. /// A `Vec`-based mesh structure used to store mesh data on the CPU.
#[derive(Clone)]
pub struct Mesh<P: Pipeline> { pub struct Mesh<P: Pipeline> {
verts: Vec<P::Vertex>, verts: Vec<P::Vertex>,
} }
@ -43,6 +44,22 @@ impl<P: Pipeline> Mesh<P> {
self.verts.push(quad.d); self.verts.push(quad.d);
self.verts.push(quad.a); self.verts.push(quad.a);
} }
/// Push the vertices of another mesh onto the end of this mesh
pub fn push_mesh(&mut self, other: &Mesh<P>) {
self.verts.extend_from_slice(other.vertices());
}
/// Push the vertices of another mesh onto the end of this mesh
pub fn push_mesh_map<F: FnMut(P::Vertex) -> P::Vertex>(&mut self, other: &Mesh<P>, mut f: F) {
// Reserve enough space in our Vec. This isn't necessary, but it tends to reduce the number
// of required (re)allocations.
self.verts.reserve(other.vertices().len());
for vert in other.vertices() {
self.verts.push(f(vert.clone()));
}
}
} }
/// Represents a triangle stored on the CPU. /// Represents a triangle stored on the CPU.

View File

@ -8,14 +8,15 @@ mod util;
// Reexports // Reexports
pub use self::{ pub use self::{
consts::Consts, consts::Consts,
mesh::{Mesh, Quad}, mesh::{Mesh, Tri, Quad},
model::Model, model::Model,
renderer::{Renderer, TgtColorFmt, TgtDepthFmt}, renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
pipelines::{ pipelines::{
Globals, Globals,
character::{ figure::{
CharacterPipeline, FigurePipeline,
Locals as CharacterLocals, Locals as FigureLocals,
BoneData as FigureBoneData,
}, },
skybox::{ skybox::{
create_mesh as create_skybox_mesh, create_mesh as create_skybox_mesh,
@ -47,7 +48,7 @@ pub enum RenderError {
/// # Examples /// # Examples
/// ///
/// - `SkyboxPipeline` /// - `SkyboxPipeline`
/// - `CharacterPipeline` /// - `FigurePipeline`
pub trait Pipeline { pub trait Pipeline {
type Vertex: type Vertex:
Clone + Clone +

View File

@ -1,47 +0,0 @@
// 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,
},
};
gfx_defines! {
vertex Vertex {
pos: [f32; 3] = "v_pos",
col: [f32; 3] = "v_col",
bone: u8 = "v_bone",
}
constant Locals {
model_mat: [[f32; 4]; 4] = "model_mat",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
}
}
pub struct CharacterPipeline;
impl Pipeline for CharacterPipeline {
type Vertex = Vertex;
}

View File

@ -0,0 +1,93 @@
// Library
use gfx::{
self,
// Macros
gfx_defines,
gfx_vertex_struct_meta,
gfx_constant_struct_meta,
gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
};
use vek::*;
// Local
use super::{
Globals,
super::{
Pipeline,
TgtColorFmt,
TgtDepthFmt,
util::arr_to_mat,
},
};
gfx_defines! {
vertex Vertex {
pos: [f32; 3] = "v_pos",
col: [f32; 3] = "v_col",
bone_idx: u8 = "v_bone_idx",
}
constant Locals {
model_mat: [[f32; 4]; 4] = "model_mat",
}
constant BoneData {
bone_mat: [[f32; 4]; 4] = "bone_mat",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
bones: gfx::ConstantBuffer<BoneData> = "u_bones",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
}
}
impl Vertex {
pub fn new(pos: Vec3<f32>, col: Rgb<f32>, bone_idx: u8) -> Self {
Self {
pos: pos.into_array(),
col: col.into_array(),
bone_idx,
}
}
pub fn with_bone_idx(mut self, bone_idx: u8) -> Self {
self.bone_idx = bone_idx;
self
}
}
impl Locals {
pub fn default() -> Self {
Self {
model_mat: arr_to_mat(Mat4::identity().into_col_array()),
}
}
}
impl BoneData {
pub fn new(bone_mat: Mat4<f32>) -> Self {
Self {
bone_mat: arr_to_mat(bone_mat.into_col_array()),
}
}
pub fn default() -> Self {
Self {
bone_mat: arr_to_mat(Mat4::identity().into_col_array()),
}
}
}
pub struct FigurePipeline;
impl Pipeline for FigurePipeline {
type Vertex = Vertex;
}

View File

@ -1,4 +1,4 @@
pub mod character; pub mod figure;
pub mod skybox; pub mod skybox;
// Library // Library

View File

@ -33,8 +33,10 @@ gfx_defines! {
pipeline pipe { pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (), vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals", locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals", globals: gfx::ConstantBuffer<Globals> = "u_globals",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color", tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::PASS_TEST, tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::PASS_TEST,
} }

View File

@ -15,13 +15,13 @@ use super::{
gfx_backend, gfx_backend,
pipelines::{ pipelines::{
Globals, Globals,
character, figure,
skybox, skybox,
}, },
}; };
/// Represents the format of the window's color target. /// Represents the format of the window's color target.
pub type TgtColorFmt = gfx::format::Srgba8; pub type TgtColorFmt = gfx::format::Rgba8;
/// Represents the format of the window's depth target. /// Represents the format of the window's depth target.
pub type TgtDepthFmt = gfx::format::DepthStencil; pub type TgtDepthFmt = gfx::format::DepthStencil;
@ -42,7 +42,7 @@ pub struct Renderer {
tgt_depth_view: TgtDepthView, tgt_depth_view: TgtDepthView,
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>, skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
character_pipeline: GfxPipeline<character::pipe::Init<'static>>, figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
} }
impl Renderer { impl Renderer {
@ -62,12 +62,12 @@ impl Renderer {
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.frag")), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.frag")),
)?; )?;
// Construct a pipeline for rendering characters // Construct a pipeline for rendering figures
let character_pipeline = create_pipeline( let figure_pipeline = create_pipeline(
&mut factory, &mut factory,
character::pipe::new(), figure::pipe::new(),
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.vert")), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/figure.vert")),
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.frag")), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/figure.frag")),
)?; )?;
Ok(Self { Ok(Self {
@ -79,7 +79,7 @@ impl Renderer {
tgt_depth_view, tgt_depth_view,
skybox_pipeline, skybox_pipeline,
character_pipeline, figure_pipeline,
}) })
} }
@ -96,28 +96,23 @@ impl Renderer {
self.device.cleanup(); self.device.cleanup();
} }
/// Create a new set of constants. /// Create a new set of constants with the provided values.
pub fn create_consts<T: Copy + gfx::traits::Pod>(&mut self) -> Result<Consts<T>, RenderError> { pub fn create_consts<T: Copy + gfx::traits::Pod>(
Ok(Consts::new(&mut self.factory))
}
/// Create a new set of constants with a value.
pub fn create_consts_with<T: Copy + gfx::traits::Pod>(
&mut self, &mut self,
val: T vals: &[T],
) -> Result<Consts<T>, RenderError> { ) -> Result<Consts<T>, RenderError> {
let mut consts = self.create_consts()?; let mut consts = Consts::new(&mut self.factory, vals.len());
consts.update(&mut self.encoder, val)?; consts.update(&mut self.encoder, vals)?;
Ok(consts) Ok(consts)
} }
/// Update a set of constants with a new value. /// Update a set of constants with the provided values.
pub fn update_consts<T: Copy + gfx::traits::Pod>( pub fn update_consts<T: Copy + gfx::traits::Pod>(
&mut self, &mut self,
consts: &mut Consts<T>, consts: &mut Consts<T>,
val: T vals: &[T]
) -> Result<(), RenderError> { ) -> Result<(), RenderError> {
consts.update(&mut self.encoder, val) consts.update(&mut self.encoder, vals)
} }
/// Create a new model from the provided mesh. /// Create a new model from the provided mesh.
@ -132,8 +127,8 @@ impl Renderer {
pub fn render_skybox( pub fn render_skybox(
&mut self, &mut self,
model: &Model<skybox::SkyboxPipeline>, model: &Model<skybox::SkyboxPipeline>,
locals: &Consts<skybox::Locals>,
globals: &Consts<Globals>, globals: &Consts<Globals>,
locals: &Consts<skybox::Locals>,
) { ) {
self.encoder.draw( self.encoder.draw(
&model.slice, &model.slice,
@ -147,6 +142,28 @@ impl Renderer {
}, },
); );
} }
/// Queue the rendering of the provided figure model in the upcoming frame.
pub fn render_figure(
&mut self,
model: &Model<figure::FigurePipeline>,
globals: &Consts<Globals>,
locals: &Consts<figure::Locals>,
bones: &Consts<figure::BoneData>,
) {
self.encoder.draw(
&model.slice,
&self.figure_pipeline.pso,
&figure::pipe::Data {
vbuf: model.vbuf.clone(),
locals: locals.buf.clone(),
globals: globals.buf.clone(),
bones: bones.buf.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> {

View File

@ -19,9 +19,9 @@ impl Camera {
/// Create a new `Camera` with default parameters. /// Create a new `Camera` with default parameters.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
focus: Vec3::zero(), focus: Vec3::unit_z() * 10.0,
ori: Vec3::zero(), ori: Vec3::zero(),
dist: 5.0, dist: 40.0,
fov: 1.3, fov: 1.3,
aspect: 1.618, aspect: 1.618,
} }

View File

@ -0,0 +1,79 @@
// Crate
use crate::{
Error,
render::{
Consts,
Globals,
Mesh,
Model,
Renderer,
FigurePipeline,
FigureBoneData,
FigureLocals,
},
anim::Skeleton,
};
pub struct Figure {
// GPU data
model: Model<FigurePipeline>,
bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>,
// CPU data
bone_meshes: [Option<Mesh<FigurePipeline>>; 16],
pub skeleton: Skeleton,
}
impl Figure {
pub fn new(
renderer: &mut Renderer,
bone_meshes: [Option<Mesh<FigurePipeline>>; 16]
) -> Result<Self, Error> {
let skeleton = Skeleton::default();
let mut this = Self {
model: renderer.create_model(&Mesh::new())?,
bone_consts: renderer.create_consts(&skeleton.compute_matrices())?,
locals: renderer.create_consts(&[FigureLocals::default()])?,
bone_meshes,
skeleton,
};
this.update_model(renderer)?;
Ok(this)
}
pub fn update_model(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
let mut mesh = Mesh::new();
self.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))
});
self.model = renderer.create_model(&mesh)?;
Ok(())
}
pub fn update_skeleton(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())?;
Ok(())
}
pub fn update_locals(&mut self, renderer: &mut Renderer, locals: FigureLocals) -> Result<(), Error> {
renderer.update_consts(&mut self.locals, &[locals])?;
Ok(())
}
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
renderer.render_figure(
&self.model,
globals,
&self.locals,
&self.bone_consts,
);
}
}

View File

@ -1,16 +1,19 @@
pub mod camera; pub mod camera;
pub mod figure;
// Standard // Standard
use std::time::Duration; use std::time::Duration;
// Library // Library
use vek::*; use vek::*;
use dot_vox;
// Project // Project
use client::{ use client::{
self, self,
Client, Client,
}; };
use common::figure::Segment;
// Crate // Crate
use crate::{ use crate::{
@ -22,47 +25,82 @@ use crate::{
Renderer, Renderer,
SkyboxPipeline, SkyboxPipeline,
SkyboxLocals, SkyboxLocals,
FigureLocals,
create_skybox_mesh, create_skybox_mesh,
}, },
window::Event, window::Event,
mesh::Meshable,
}; };
// Local // Local
use self::camera::Camera; use self::{
camera::Camera,
figure::Figure,
};
// TODO: Don't hard-code this
const CURSOR_PAN_SCALE: f32 = 0.005;
struct Skybox { struct Skybox {
model: Model<SkyboxPipeline>, model: Model<SkyboxPipeline>,
locals: Consts<SkyboxLocals>, locals: Consts<SkyboxLocals>,
} }
// TODO: Don't hard-code this
const CURSOR_PAN_SCALE: f32 = 0.005;
pub struct Scene { pub struct Scene {
camera: Camera, camera: Camera,
globals: Consts<Globals>, globals: Consts<Globals>,
skybox: Skybox, skybox: Skybox,
test_figure: Figure,
client: Client, client: Client,
} }
// TODO: Make a proper asset loading system
fn load_segment(filename: &'static str) -> Segment {
Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/test_assets/").to_string() + filename)).unwrap())
}
impl Scene { impl Scene {
/// Create a new `Scene` with default parameters. /// Create a new `Scene` with default parameters.
pub fn new(renderer: &mut Renderer) -> Self { pub fn new(renderer: &mut Renderer) -> Self {
Self { Self {
camera: Camera::new(), camera: Camera::new(),
globals: renderer globals: renderer
.create_consts_with(Globals::default()) .create_consts(&[Globals::default()])
.unwrap(), .unwrap(),
skybox: Skybox { skybox: Skybox {
model: renderer model: renderer
.create_model(&create_skybox_mesh()) .create_model(&create_skybox_mesh())
.unwrap(), .unwrap(),
locals: renderer locals: renderer
.create_consts_with(SkyboxLocals::default()) .create_consts(&[SkyboxLocals::default()])
.unwrap(), .unwrap(),
}, },
test_figure: Figure::new(
renderer,
[
Some(load_segment("head.vox").generate_mesh_with_offset(Vec3::new(-7.0, -5.5, -1.0))),
Some(load_segment("chest.vox").generate_mesh_with_offset(Vec3::new(-6.0, -3.0, 0.0))),
Some(load_segment("belt.vox").generate_mesh_with_offset(Vec3::new(-5.0, -3.0, 0.0))),
Some(load_segment("pants.vox").generate_mesh_with_offset(Vec3::new(-5.0, -3.0, 0.0))),
Some(load_segment("foot.vox").generate_mesh_with_offset(Vec3::new(-2.5, -3.0, 0.0))),
Some(load_segment("foot.vox").generate_mesh_with_offset(Vec3::new(-2.5, -3.0, 0.0))),
Some(load_segment("hand.vox").generate_mesh_with_offset(Vec3::new(-2.0, -2.0, -1.0))),
Some(load_segment("hand.vox").generate_mesh_with_offset(Vec3::new(-2.0, -2.0, -1.0))),
Some(load_segment("sword.vox").generate_mesh_with_offset(Vec3::new(-6.5, -1.0, 0.0))),
None,
None,
None,
None,
None,
None,
None,
],
)
.unwrap(),
client: Client::new(), client: Client::new(),
} }
} }
@ -92,7 +130,7 @@ impl Scene {
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(); let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents();
// Update global constants // Update global constants
renderer.update_consts(&mut self.globals, Globals::new( renderer.update_consts(&mut self.globals, &[Globals::new(
view_mat, view_mat,
proj_mat, proj_mat,
cam_pos, cam_pos,
@ -100,8 +138,32 @@ impl Scene {
10.0, 10.0,
self.client.state().get_time_of_day(), self.client.state().get_time_of_day(),
0.0, 0.0,
)) )])
.expect("Failed to update global constants"); .expect("Failed to update global constants");
// TODO: Don't do this here
let offs = (self.client.state().get_tick() as f32 * 10.0).sin();
self.test_figure.skeleton.bone_mut(0).offset = Vec3::new(0.0, 0.0, 13.0);
self.test_figure.skeleton.bone_mut(0).ori = Quaternion::rotation_z(offs * 0.3);
// Chest
self.test_figure.skeleton.bone_mut(1).offset = Vec3::new(0.0, 0.0, 9.0);
self.test_figure.skeleton.bone_mut(2).offset = Vec3::new(0.0, 0.0, 7.0);
self.test_figure.skeleton.bone_mut(3).offset = Vec3::new(0.0, 0.0, 4.0);
self.test_figure.skeleton.bone_mut(1).ori = Quaternion::rotation_z(offs * 0.15);
self.test_figure.skeleton.bone_mut(2).ori = Quaternion::rotation_z(offs * 0.15);
self.test_figure.skeleton.bone_mut(3).ori = Quaternion::rotation_z(offs * 0.15);
//Feet
self.test_figure.skeleton.bone_mut(4).offset = Vec3::new(-3.0, -offs * 4.0, 0.0);
self.test_figure.skeleton.bone_mut(5).offset = Vec3::new(3.0, offs * 4.0, 0.0);
// Hands
self.test_figure.skeleton.bone_mut(6).offset = Vec3::new(-8.0, offs * 4.0, 9.0);
self.test_figure.skeleton.bone_mut(7).offset = Vec3::new(8.0, -offs * 4.0, 9.0);
// Sword
self.test_figure.skeleton.bone_mut(8).offset = Vec3::new(-8.0, 5.0, 24.0);
self.test_figure.skeleton.bone_mut(8).ori = Quaternion::rotation_y(2.5);
self.test_figure.update_locals(renderer, FigureLocals::default());
self.test_figure.update_skeleton(renderer);
} }
/// Render the scene using the provided `Renderer` /// Render the scene using the provided `Renderer`
@ -109,8 +171,11 @@ impl Scene {
// Render the skybox first (it appears over everything else so must be rendered first) // Render the skybox first (it appears over everything else so must be rendered first)
renderer.render_skybox( renderer.render_skybox(
&self.skybox.model, &self.skybox.model,
&self.skybox.locals,
&self.globals, &self.globals,
&self.skybox.locals,
); );
// Render the test figure
self.test_figure.render(renderer, &self.globals);
} }
} }

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.