Initial block model support
This commit is contained in:
parent
73646421d3
commit
12b1dd6445
|
@ -6,6 +6,8 @@ use std::io::Write;
|
|||
use byteorder::{WriteBytesExt, NativeEndian};
|
||||
use world;
|
||||
use render;
|
||||
use resources;
|
||||
use model;
|
||||
|
||||
const NUM_WORKERS: usize = 8;
|
||||
|
||||
|
@ -16,19 +18,25 @@ pub struct ChunkBuilder {
|
|||
|
||||
sections: Vec<(i32, i32, i32, Arc<world::SectionKey>)>,
|
||||
next_collection: f64,
|
||||
|
||||
models: Arc<RwLock<model::Factory>>,
|
||||
resources: Arc<RwLock<resources::Manager>>,
|
||||
resource_version: usize,
|
||||
}
|
||||
|
||||
impl ChunkBuilder {
|
||||
pub fn new(textures: Arc<RwLock<render::TextureManager>>) -> ChunkBuilder {
|
||||
pub fn new(resources: Arc<RwLock<resources::Manager>>, textures: Arc<RwLock<render::TextureManager>>) -> ChunkBuilder {
|
||||
let models = Arc::new(RwLock::new(model::Factory::new(resources.clone(), textures)));
|
||||
|
||||
let mut threads = vec![];
|
||||
let mut free = vec![];
|
||||
let (built_send, built_recv) = mpsc::channel();
|
||||
for i in 0 .. NUM_WORKERS {
|
||||
let built_send = built_send.clone();
|
||||
let (work_send, work_recv) = mpsc::channel();
|
||||
let textures = textures.clone();
|
||||
let models = models.clone();
|
||||
let id = i;
|
||||
threads.push((work_send, thread::spawn(move || build_func(id, textures, work_recv, built_send))));
|
||||
threads.push((work_send, thread::spawn(move || build_func(id, models, work_recv, built_send))));
|
||||
free.push((i, vec![]));
|
||||
}
|
||||
ChunkBuilder {
|
||||
|
@ -37,11 +45,23 @@ impl ChunkBuilder {
|
|||
built_recv: built_recv,
|
||||
sections: vec![],
|
||||
next_collection: 0.0,
|
||||
models: models,
|
||||
resources: resources.clone(),
|
||||
resource_version: 0xFFFF,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, world: &mut world::World, renderer: &mut render::Renderer, delta: f64) {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
{
|
||||
let rm = self.resources.read().unwrap();
|
||||
if rm.version() != self.resource_version {
|
||||
self.resource_version = rm.version();
|
||||
self.models.write().unwrap().clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
while let Ok((id, mut val)) = self.built_recv.try_recv() {
|
||||
world.reset_building_flag(val.position);
|
||||
|
||||
|
@ -109,7 +129,8 @@ struct BuildReply {
|
|||
key: Arc<world::SectionKey>,
|
||||
}
|
||||
|
||||
fn build_func(id: usize, textures: Arc<RwLock<render::TextureManager>>, work_recv: mpsc::Receiver<BuildReq>, built_send: mpsc::Sender<(usize, BuildReply)>) {
|
||||
fn build_func(id: usize, models: Arc<RwLock<model::Factory>>, work_recv: mpsc::Receiver<BuildReq>, built_send: mpsc::Sender<(usize, BuildReply)>) {
|
||||
use rand::{self, Rng, SeedableRng};
|
||||
loop {
|
||||
let BuildReq {
|
||||
snapshot,
|
||||
|
@ -121,71 +142,35 @@ fn build_func(id: usize, textures: Arc<RwLock<render::TextureManager>>, work_rec
|
|||
Err(_) => return,
|
||||
};
|
||||
|
||||
let mut rng = rand::XorShiftRng::from_seed([
|
||||
position.0 as u32,
|
||||
position.1 as u32,
|
||||
position.2 as u32,
|
||||
(position.0 as u32 ^ position.2 as u32) | 1,
|
||||
]);
|
||||
|
||||
let mut solid_buffer = buffer;
|
||||
let mut solid_count = 0;
|
||||
|
||||
for y in 0 .. 16 {
|
||||
for x in 0 .. 16 {
|
||||
for z in 0 .. 16 {
|
||||
let block = snapshot.get_block(x, y, z).get_material();
|
||||
if !block.renderable {
|
||||
let block = snapshot.get_block(x, y, z);
|
||||
if !block.get_material().renderable {
|
||||
// Use one step of the rng so that
|
||||
// if a block is placed in an empty
|
||||
// location is variant doesn't change
|
||||
rng.next_u32();
|
||||
continue;
|
||||
}
|
||||
|
||||
for dir in Direction::all() {
|
||||
|
||||
let offset = dir.get_offset();
|
||||
let other = snapshot.get_block(x + offset.0, y + offset.1, z + offset.2).get_material();
|
||||
if other.renderable {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (mut cr, mut cg, mut cb) = (255, 255, 255);
|
||||
if dir == Direction::West || dir == Direction::East {
|
||||
cr = ((cr as f64) * 0.8) as u8;
|
||||
cg = ((cg as f64) * 0.8) as u8;
|
||||
cb = ((cb as f64) * 0.8) as u8;
|
||||
}
|
||||
|
||||
let stone = render::Renderer::get_texture(&textures, &format!("minecraft:blocks/{}", block.texture));
|
||||
solid_count += 6;
|
||||
for vert in dir.get_verts() {
|
||||
let mut vert = vert.clone();
|
||||
// TODO
|
||||
vert.r = cr;
|
||||
vert.g = cg;
|
||||
vert.b = cb;
|
||||
|
||||
vert.x += x as f32;
|
||||
vert.y += y as f32;
|
||||
vert.z += z as f32;
|
||||
|
||||
vert.toffsetx *= stone.get_width() as i16 * 16;
|
||||
vert.toffsety *= stone.get_height() as i16 * 16;
|
||||
|
||||
let (bl, sl) = calculate_light(
|
||||
&snapshot,
|
||||
x, y, z,
|
||||
vert.x as f64,
|
||||
vert.y as f64,
|
||||
vert.z as f64,
|
||||
dir,
|
||||
true,
|
||||
false
|
||||
);
|
||||
vert.block_light = bl;
|
||||
vert.sky_light = sl;
|
||||
|
||||
// TODO
|
||||
vert.tatlas = stone.atlas as i16;
|
||||
vert.tx = stone.get_x() as u16;
|
||||
vert.ty = stone.get_y() as u16;
|
||||
vert.tw = stone.get_width() as u16;
|
||||
vert.th = stone.get_height() as u16;
|
||||
|
||||
vert.write(&mut solid_buffer);
|
||||
}
|
||||
}
|
||||
// TODO Liquids need a special case
|
||||
let model_name = block.get_model();
|
||||
let variant = block.get_model_variant();
|
||||
solid_count += model::Factory::get_state_model(
|
||||
&models, &model_name.0, &model_name.1, &variant, &mut rng,
|
||||
&snapshot, x, y, z, &mut solid_buffer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,58 +184,9 @@ fn build_func(id: usize, textures: Arc<RwLock<render::TextureManager>>, work_rec
|
|||
}
|
||||
}
|
||||
|
||||
fn calculate_light(snapshot: &world::Snapshot, orig_x: i32, orig_y: i32, orig_z: i32,
|
||||
x: f64, y: f64, z: f64, face: Direction, smooth: bool, force: bool) -> (u16, u16) {
|
||||
use std::cmp::max;
|
||||
use world::block;
|
||||
let (ox, oy, oz) = if !snapshot.get_block(orig_x, orig_y, orig_z).get_material().renderable { // TODO: cull check
|
||||
(0, 0, 0)
|
||||
} else {
|
||||
face.get_offset()
|
||||
};
|
||||
|
||||
let s_block_light = snapshot.get_block_light(orig_x + ox, orig_y + oy, orig_z + oz);
|
||||
let s_sky_light = snapshot.get_sky_light(orig_x + ox, orig_y + oy, orig_z + oz);
|
||||
if !smooth {
|
||||
return ((s_block_light as u16) * 4000, (s_sky_light as u16) * 4000);
|
||||
}
|
||||
|
||||
let mut block_light = 0u32;
|
||||
let mut sky_light = 0u32;
|
||||
let mut count = 0;
|
||||
|
||||
let s_block_light = max(((s_block_light as i8) - 8), 0) as u8;
|
||||
let s_sky_light = max(((s_sky_light as i8) - 8), 0) as u8;
|
||||
|
||||
let dx = (ox as f64) * 0.6;
|
||||
let dy = (oy as f64) * 0.6;
|
||||
let dz = (oz as f64) * 0.6;
|
||||
|
||||
for ox in [-0.6, 0.0].into_iter() {
|
||||
for oy in [-0.6, 0.0].into_iter() {
|
||||
for oz in [-0.6, 0.0].into_iter() {
|
||||
let lx = (x + ox + dx).round() as i32;
|
||||
let ly = (y + oy + dy).round() as i32;
|
||||
let lz = (z + oz + dz).round() as i32;
|
||||
let mut bl = snapshot.get_block_light(lx, ly, lz);
|
||||
let mut sl = snapshot.get_sky_light(lx, ly, lz);
|
||||
if (force && match snapshot.get_block(lx, ly, lz) { block::Air{} => false, _ => true })
|
||||
|| (sl == 0 && bl == 0){
|
||||
bl = s_block_light;
|
||||
sl = s_sky_light;
|
||||
}
|
||||
block_light += bl as u32;
|
||||
sky_light += sl as u32;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((((block_light * 4000) / count) as u16), (((sky_light * 4000) / count) as u16))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
Invalid,
|
||||
Up,
|
||||
Down,
|
||||
North,
|
||||
|
@ -268,6 +204,18 @@ impl Direction {
|
|||
]
|
||||
}
|
||||
|
||||
pub fn from_string(val: &str) -> Direction {
|
||||
match val {
|
||||
"up" => Direction::Up,
|
||||
"down" => Direction::Down,
|
||||
"north" => Direction::North,
|
||||
"south" => Direction::South,
|
||||
"west" => Direction::West,
|
||||
"east" => Direction::East,
|
||||
_ => Direction::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_verts(&self) -> &'static [BlockVertex; 4] {
|
||||
match *self {
|
||||
Direction::Up => PRECOMPUTED_VERTS[0],
|
||||
|
@ -276,6 +224,7 @@ impl Direction {
|
|||
Direction::South => PRECOMPUTED_VERTS[3],
|
||||
Direction::West => PRECOMPUTED_VERTS[4],
|
||||
Direction::East => PRECOMPUTED_VERTS[5],
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,6 +236,31 @@ impl Direction {
|
|||
Direction::South => (0, 0, 1),
|
||||
Direction::West => (-1, 0, 0),
|
||||
Direction::East => (1, 0, 0),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> &'static str {
|
||||
match *self {
|
||||
Direction::Up => "up",
|
||||
Direction::Down => "down",
|
||||
Direction::North => "north",
|
||||
Direction::South => "south",
|
||||
Direction::West => "west",
|
||||
Direction::East => "east",
|
||||
Direction::Invalid => "invalid",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
match *self {
|
||||
Direction::Up => 0,
|
||||
Direction::Down => 1,
|
||||
Direction::North => 2,
|
||||
Direction::South => 3,
|
||||
Direction::West => 4,
|
||||
Direction::East => 5,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,11 +306,21 @@ const PRECOMPUTED_VERTS: [&'static [BlockVertex; 4]; 6] = [
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct BlockVertex {
|
||||
x: f32, y: f32, z: f32,
|
||||
tx: u16, ty: u16, tw: u16, th: u16,
|
||||
toffsetx: i16, toffsety: i16, tatlas: i16,
|
||||
r: u8, g: u8, b: u8,
|
||||
block_light: u16, sky_light: u16,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
pub tx: u16,
|
||||
pub ty: u16,
|
||||
pub tw: u16,
|
||||
pub th: u16,
|
||||
pub toffsetx: i16,
|
||||
pub toffsety: i16,
|
||||
pub tatlas: i16,
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub block_light: u16,
|
||||
pub sky_light: u16,
|
||||
}
|
||||
|
||||
impl BlockVertex {
|
||||
|
@ -349,7 +333,7 @@ impl BlockVertex {
|
|||
block_light: 0, sky_light: 0,
|
||||
}
|
||||
}
|
||||
fn write<W: Write>(&self, w: &mut W) {
|
||||
pub fn write<W: Write>(&self, w: &mut W) {
|
||||
let _ = w.write_f32::<NativeEndian>(self.x);
|
||||
let _ = w.write_f32::<NativeEndian>(self.y);
|
||||
let _ = w.write_f32::<NativeEndian>(self.z);
|
||||
|
|
|
@ -53,6 +53,7 @@ pub mod server;
|
|||
pub mod world;
|
||||
pub mod chunk_builder;
|
||||
pub mod auth;
|
||||
pub mod model;
|
||||
|
||||
use std::sync::{Arc, RwLock, Mutex};
|
||||
use std::marker::PhantomData;
|
||||
|
@ -191,11 +192,11 @@ fn main() {
|
|||
focused: false,
|
||||
renderer: renderer,
|
||||
screen_sys: screen_sys,
|
||||
resource_manager: resource_manager,
|
||||
resource_manager: resource_manager.clone(),
|
||||
console: con,
|
||||
should_close: false,
|
||||
mouse_pos: (0, 0),
|
||||
chunk_builder: chunk_builder::ChunkBuilder::new(textures),
|
||||
chunk_builder: chunk_builder::ChunkBuilder::new(resource_manager, textures),
|
||||
connect_reply: None,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,737 @@
|
|||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use resources;
|
||||
use render;
|
||||
use world;
|
||||
use chunk_builder::{self, Direction};
|
||||
use serde_json;
|
||||
|
||||
use std::hash::BuildHasherDefault;
|
||||
use types::hash::FNVHash;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
pub struct Factory {
|
||||
resources: Arc<RwLock<resources::Manager>>,
|
||||
textures: Arc<RwLock<render::TextureManager>>,
|
||||
|
||||
models: HashMap<Key, StateModel, BuildHasherDefault<FNVHash>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
struct Key(String, String);
|
||||
|
||||
macro_rules! try_log {
|
||||
($e:expr) => (
|
||||
match $e {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
error!("Error loading model {:?}", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
(opt $e:expr) => (
|
||||
match $e {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
error!("Error loading model {:?}", err);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
impl Factory {
|
||||
pub fn new(resources: Arc<RwLock<resources::Manager>>, textures: Arc<RwLock<render::TextureManager>>) -> Factory {
|
||||
Factory {
|
||||
resources: resources,
|
||||
textures: textures,
|
||||
|
||||
models: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_cache(&mut self) {
|
||||
self.models.clear();
|
||||
}
|
||||
|
||||
pub fn get_state_model<R: Rng, W: Write>(models: &Arc<RwLock<Factory>>, plugin: &str, name: &str, variant: &str, rng: &mut R,
|
||||
snapshot: &world::Snapshot, x: i32, y: i32, z: i32, buf: &mut W) -> usize {
|
||||
let key = Key(plugin.to_owned(), name.to_owned());
|
||||
let mut missing = false;
|
||||
{
|
||||
let m = models.read().unwrap();
|
||||
if let Some(model) = m.models.get(&key) {
|
||||
if let Some(var) = model.get_variants(variant) {
|
||||
let model = var.choose_model(rng);
|
||||
return model.render(snapshot, x, y, z, buf);
|
||||
}
|
||||
missing = true;
|
||||
}
|
||||
}
|
||||
if !missing {
|
||||
let mut m = models.write().unwrap();
|
||||
if !m.models.contains_key(&key) {
|
||||
if !m.load_model(plugin, name) {
|
||||
error!("Error loading model {}:{}", plugin, name);
|
||||
}
|
||||
}
|
||||
if let Some(model) = m.models.get(&key) {
|
||||
if let Some(var) = model.get_variants(variant) {
|
||||
let model = var.choose_model(rng);
|
||||
return model.render(snapshot, x, y, z, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
let ret = Factory::get_state_model(models, "steven", "missing_block", "normal", rng, snapshot, x, y, z, buf);
|
||||
{
|
||||
let mut m = models.write().unwrap();
|
||||
let model = m.models.get(&Key("steven".to_owned(), "missing_block".to_owned())).unwrap().clone();
|
||||
m.models.insert(key, model);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn load_model(&mut self, plugin: &str, name: &str) -> bool {
|
||||
let file = match self.resources.read().unwrap().open(plugin, &format!("blockstates/{}.json", name)) {
|
||||
Some(val) => val,
|
||||
None => return false,
|
||||
};
|
||||
let mdl: serde_json::Value = try_log!(serde_json::from_reader(file));
|
||||
|
||||
let mut model = StateModel {
|
||||
variants: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
};
|
||||
|
||||
if let Some(variants) = mdl.find("variants").and_then(|v| v.as_object()) {
|
||||
for (k, v) in variants {
|
||||
let vars = self.parse_model_list(plugin, v);
|
||||
if vars.models.is_empty() {
|
||||
return false;
|
||||
}
|
||||
model.variants.insert(k.clone(), vars);
|
||||
}
|
||||
}
|
||||
if let Some(multipart) = mdl.find("multipart").and_then(|v| v.as_array()) {
|
||||
warn!("Found unhandled multipart {:?}", multipart);
|
||||
}
|
||||
|
||||
self.models.insert(Key(plugin.to_owned(), name.to_owned()), model);
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_model_list(&self, plugin: &str, v: &serde_json::Value) -> Variants {
|
||||
let mut variants = Variants {
|
||||
models: vec![]
|
||||
};
|
||||
if let Some(list) = v.as_array() {
|
||||
for val in list {
|
||||
if let Some(mdl) = self.parse_block_state_variant(plugin, val) {
|
||||
variants.models.push(self.process_model(mdl));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(mdl) = self.parse_block_state_variant(plugin, v) {
|
||||
variants.models.push(self.process_model(mdl));
|
||||
}
|
||||
}
|
||||
variants
|
||||
}
|
||||
|
||||
fn parse_block_state_variant(&self, plugin: &str, v: &serde_json::Value) -> Option<RawModel> {
|
||||
let model_name = match v.find("model").and_then(|v| v.as_string()) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
error!("Couldn't find model name");
|
||||
return None;
|
||||
},
|
||||
};
|
||||
|
||||
let file = match self.resources.read().unwrap().open(plugin, &format!("models/block/{}.json", model_name)) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
error!("Couldn't find model {}", format!("models/block/{}.json", model_name));
|
||||
return None;
|
||||
},
|
||||
};
|
||||
let block_model: serde_json::Value = try_log!(opt serde_json::from_reader(file));
|
||||
|
||||
let mut model = match self.parse_model(plugin, &block_model) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
error!("Failed to parse model {}", format!("models/block/{}.json", model_name));
|
||||
return None;
|
||||
},
|
||||
};
|
||||
|
||||
model.y = v.find("y").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
model.x = v.find("x").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
model.uvlock = v.find("uvlock").and_then(|v| v.as_boolean()).unwrap_or(false);
|
||||
model.weight = v.find("weight").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
||||
Some(model)
|
||||
}
|
||||
|
||||
fn parse_model(&self, plugin: &str, v: &serde_json::Value) -> Option<RawModel> {
|
||||
let parent = v.find("parent").and_then(|v| v.as_string()).unwrap_or("");
|
||||
let mut model = if !parent.is_empty() && !parent.starts_with("builtin/") {
|
||||
let file = match self.resources.read().unwrap().open(plugin, &format!("models/{}.json", parent)) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
error!("Couldn't find model {}", format!("models/{}.json", parent));
|
||||
return None;
|
||||
},
|
||||
};
|
||||
let block_model: serde_json::Value = try_log!(opt serde_json::from_reader(file));
|
||||
let model = match self.parse_model(plugin, &block_model) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
error!("Failed to parse model {}", format!("models/{}.json", parent));
|
||||
return None
|
||||
},
|
||||
};
|
||||
model
|
||||
} else {
|
||||
RawModel {
|
||||
texture_vars: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
elements: vec![],
|
||||
ambient_occlusion: true,
|
||||
ao_set: false,
|
||||
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
uvlock: false,
|
||||
weight: 1.0,
|
||||
|
||||
display: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
builtin: match parent {
|
||||
"builtin/generated" => BuiltinType::Generated,
|
||||
"builtin/entity" => BuiltinType::Entity,
|
||||
"builtin/compass" => BuiltinType::Compass,
|
||||
"builtin/clock" => BuiltinType::Clock,
|
||||
_ => BuiltinType::False,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(textures) = v.find("textures").and_then(|v| v.as_object()) {
|
||||
for (k, v) in textures {
|
||||
model.texture_vars.insert(k.clone(), v.as_string().unwrap_or("").to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ao) = v.find("ambientocclusion").and_then(|v| v.as_boolean()) {
|
||||
model.ambient_occlusion = ao;
|
||||
model.ao_set = true;
|
||||
} else if !model.ao_set {
|
||||
model.ambient_occlusion = true;
|
||||
}
|
||||
|
||||
if let Some(elements) = v.find("elements").and_then(|v| v.as_array()) {
|
||||
for e in elements {
|
||||
model.elements.push(self.parse_block_element(e));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Display
|
||||
|
||||
Some(model)
|
||||
}
|
||||
|
||||
fn parse_block_element(&self, v: &serde_json::Value) -> ModelElement {
|
||||
let mut element = ModelElement {
|
||||
from: v.find("from").and_then(|v| v.as_array()).map(|v| [
|
||||
v[0].as_f64().unwrap(),
|
||||
v[1].as_f64().unwrap(),
|
||||
v[2].as_f64().unwrap()
|
||||
]).unwrap(),
|
||||
to: v.find("to").and_then(|v| v.as_array()).map(|v| [
|
||||
v[0].as_f64().unwrap(),
|
||||
v[1].as_f64().unwrap(),
|
||||
v[2].as_f64().unwrap()
|
||||
]).unwrap(),
|
||||
shade: v.find("shade").and_then(|v| v.as_boolean()).unwrap_or(false),
|
||||
faces: [None, None, None, None, None, None],
|
||||
rotation: None,
|
||||
};
|
||||
if let Some(faces) = v.find("faces").and_then(|v| v.as_object()) {
|
||||
for dir in Direction::all() {
|
||||
if let Some(face) = faces.get(dir.as_string()) {
|
||||
element.faces[dir.index()] = Some(BlockFace {
|
||||
uv: face.find("uv").and_then(|v| v.as_array()).map(|v| [
|
||||
v[0].as_f64().unwrap(),
|
||||
v[1].as_f64().unwrap(),
|
||||
v[2].as_f64().unwrap(),
|
||||
v[3].as_f64().unwrap()
|
||||
]).unwrap_or_else(|| {
|
||||
let mut uv = [0.0, 0.0, 16.0, 16.0];
|
||||
match dir {
|
||||
Direction::North | Direction::South => {
|
||||
uv[0] = element.from[0];
|
||||
uv[2] = element.to[0];
|
||||
uv[1] = 16.0 - element.to[1];
|
||||
uv[3] = 16.0 - element.from[1];
|
||||
},
|
||||
Direction::West | Direction::East => {
|
||||
uv[0] = element.from[2];
|
||||
uv[2] = element.to[2];
|
||||
uv[1] = 16.0 - element.to[1];
|
||||
uv[3] = 16.0 - element.from[1];
|
||||
},
|
||||
Direction::Down | Direction::Up => {
|
||||
uv[0] = element.from[0];
|
||||
uv[2] = element.to[0];
|
||||
uv[1] = 16.0 - element.to[2];
|
||||
uv[3] = 16.0 - element.from[2];
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
uv
|
||||
}),
|
||||
texture: face.find("texture")
|
||||
.and_then(|v| v.as_string())
|
||||
.map(|v| if v.starts_with('#') {
|
||||
v.to_owned()
|
||||
} else {
|
||||
"#".to_owned() + v
|
||||
}).unwrap(),
|
||||
cull_face: Direction::from_string(
|
||||
face.find("cullface")
|
||||
.and_then(|v| v.as_string())
|
||||
.unwrap_or("invalid")
|
||||
),
|
||||
rotation: face.find("rotation")
|
||||
.and_then(|v| v.as_i64())
|
||||
.map(|v| v as i32)
|
||||
.unwrap_or(0),
|
||||
tint_index: face.find("tintindex")
|
||||
.and_then(|v| v.as_i64())
|
||||
.map(|v| v as i32)
|
||||
.unwrap_or(-1),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rotation) = v.find("rotation") {
|
||||
element.rotation = Some(BlockRotation {
|
||||
origin: rotation.find("origin").and_then(|v| v.as_array()).map(|v| [
|
||||
v[0].as_f64().unwrap(),
|
||||
v[1].as_f64().unwrap(),
|
||||
v[2].as_f64().unwrap()
|
||||
]).unwrap_or([8.0, 8.0, 8.0]),
|
||||
axis: rotation.find("axis").and_then(|v| v.as_string()).unwrap_or("").to_owned(),
|
||||
angle: rotation.find("angle").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
rescale: rotation.find("rescale").and_then(|v| v.as_boolean()).unwrap_or(false),
|
||||
});
|
||||
}
|
||||
|
||||
element
|
||||
}
|
||||
|
||||
fn process_model(&self, mut raw: RawModel) -> Model {
|
||||
let mut model = Model {
|
||||
faces: vec![],
|
||||
ambient_occlusion: raw.ambient_occlusion,
|
||||
weight: raw.weight,
|
||||
};
|
||||
let elements = ::std::mem::replace(&mut raw.elements, vec![]);
|
||||
for el in elements {
|
||||
let all_dirs = Direction::all();
|
||||
for (i, face) in el.faces.iter().enumerate() {
|
||||
if let Some(face) = face.as_ref() {
|
||||
let face_id = all_dirs[i];
|
||||
let mut processed_face = Face {
|
||||
cull_face: face.cull_face,
|
||||
facing: face_id,
|
||||
vertices: vec![],
|
||||
vertices_texture: vec![],
|
||||
indices: 0,
|
||||
shade: el.shade,
|
||||
tint_index: face.tint_index,
|
||||
};
|
||||
if raw.x > 0.0 {
|
||||
let o = (raw.x as i32) / 90;
|
||||
processed_face.cull_face = rotate_direction(processed_face.cull_face, o, FACE_ROTATION_X, &[
|
||||
Direction::East,
|
||||
Direction::West,
|
||||
Direction::Invalid,
|
||||
]);
|
||||
processed_face.facing = rotate_direction(processed_face.facing, o, FACE_ROTATION_X, &[
|
||||
Direction::East,
|
||||
Direction::West,
|
||||
Direction::Invalid,
|
||||
]);
|
||||
}
|
||||
if raw.y > 0.0 {
|
||||
let o = (raw.y as i32) / 90;
|
||||
processed_face.cull_face = rotate_direction(processed_face.cull_face, o, FACE_ROTATION, &[
|
||||
Direction::Up,
|
||||
Direction::Down,
|
||||
Direction::Invalid,
|
||||
]);
|
||||
processed_face.facing = rotate_direction(processed_face.facing, o, FACE_ROTATION, &[
|
||||
Direction::Up,
|
||||
Direction::Down,
|
||||
Direction::Invalid,
|
||||
]);
|
||||
}
|
||||
|
||||
let mut verts = all_dirs[i].get_verts().to_vec();
|
||||
let texture_name = raw.lookup_texture(&face.texture);
|
||||
let texture = render::Renderer::get_texture(&self.textures, &texture_name);
|
||||
|
||||
let mut ux1 = (face.uv[0] * (texture.get_width() as f64)) as i16;
|
||||
let mut ux2 = (face.uv[2] * (texture.get_width() as f64)) as i16;
|
||||
let mut uy1 = (face.uv[1] * (texture.get_height() as f64)) as i16;
|
||||
let mut uy2 = (face.uv[3] * (texture.get_height() as f64)) as i16;
|
||||
|
||||
let tw = texture.get_width() as i16;
|
||||
let th = texture.get_height() as i16;
|
||||
if face.rotation > 0 {
|
||||
let x = ux1;
|
||||
let y = uy1;
|
||||
let w = ux2 - ux1;
|
||||
let h = uy2 - uy1;
|
||||
match face.rotation {
|
||||
90 => {
|
||||
uy2 = x + w;
|
||||
ux1 = tw*16 - (y + h);
|
||||
ux2 = tw*16 - y;
|
||||
uy1 = x;
|
||||
},
|
||||
180 => {
|
||||
uy1 = th*16 - (y + h);
|
||||
uy2 = th*16 - y;
|
||||
ux1 = x + w;
|
||||
ux2 = x;
|
||||
},
|
||||
270 => {
|
||||
uy2 = x;
|
||||
uy1 = x + w;
|
||||
ux2 = y + h;
|
||||
ux1 = y;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
let mut min_x = ::std::f64::INFINITY;
|
||||
let mut min_y = ::std::f64::INFINITY;
|
||||
let mut min_z = ::std::f64::INFINITY;
|
||||
let mut max_x = ::std::f64::NEG_INFINITY;
|
||||
let mut max_y = ::std::f64::NEG_INFINITY;
|
||||
let mut max_z = ::std::f64::NEG_INFINITY;
|
||||
|
||||
for v in &mut verts {
|
||||
processed_face.vertices_texture.push(texture.clone());
|
||||
v.tx = texture.get_x() as u16;
|
||||
v.ty = texture.get_y() as u16;
|
||||
v.tw = texture.get_width() as u16;
|
||||
v.th = texture.get_height() as u16;
|
||||
v.tatlas = texture.atlas as i16;
|
||||
|
||||
if v.x < 0.5 {
|
||||
v.x = (el.from[0] / 16.0) as f32;
|
||||
} else {
|
||||
v.x = (el.to[0] / 16.0) as f32;
|
||||
}
|
||||
|
||||
if v.y < 0.5 {
|
||||
v.y = (el.from[1] / 16.0) as f32;
|
||||
} else {
|
||||
v.y = (el.to[1] / 16.0) as f32;
|
||||
}
|
||||
|
||||
if v.z < 0.5 {
|
||||
v.z = (el.from[2] / 16.0) as f32;
|
||||
} else {
|
||||
v.z = (el.to[2] / 16.0) as f32;
|
||||
}
|
||||
|
||||
if let Some(r) = el.rotation.as_ref() {
|
||||
warn!("NYI rotation");
|
||||
}
|
||||
|
||||
if raw.x > 0.0 {
|
||||
warn!("NYI rotation x");
|
||||
}
|
||||
|
||||
if raw.y > 0.0 {
|
||||
let rot_y = (raw.y * (::std::f64::consts::PI / 180.0)) as f32;
|
||||
let c = rot_y.cos();
|
||||
let s = rot_y.sin();
|
||||
let x = v.x - 0.5;
|
||||
let z = v.z - 0.5;
|
||||
v.x = 0.5 + (x*c - z*s);
|
||||
v.z = 0.5 + (z*c + x*s);
|
||||
}
|
||||
|
||||
if v.toffsetx == 0 {
|
||||
v.toffsetx = ux1;
|
||||
} else {
|
||||
v.toffsetx = ux2;
|
||||
}
|
||||
|
||||
if v.toffsety == 0 {
|
||||
v.toffsety = uy1;
|
||||
} else {
|
||||
v.toffsety = uy2;
|
||||
}
|
||||
|
||||
if face.rotation > 0 {
|
||||
warn!("NYI face rotation");
|
||||
}
|
||||
|
||||
// TODO: More rotation stuff
|
||||
}
|
||||
|
||||
processed_face.vertices = verts;
|
||||
processed_face.indices = 6;
|
||||
model.faces.push(processed_face);
|
||||
}
|
||||
}
|
||||
}
|
||||
model
|
||||
}
|
||||
}
|
||||
|
||||
const FACE_ROTATION: &'static [Direction] = &[
|
||||
Direction::North,
|
||||
Direction::East,
|
||||
Direction::South,
|
||||
Direction::West,
|
||||
];
|
||||
|
||||
const FACE_ROTATION_X: &'static [Direction] = &[
|
||||
Direction::North,
|
||||
Direction::Down,
|
||||
Direction::South,
|
||||
Direction::Up,
|
||||
];
|
||||
|
||||
fn rotate_direction(val: Direction, offset: i32, rots: &[Direction], invalid: &[Direction]) -> Direction {
|
||||
for d in invalid {
|
||||
if *d == val {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
let pos = rots.iter()
|
||||
.position(|v| *v == val)
|
||||
.unwrap_or(0);
|
||||
rots[(pos + offset as usize) % rots.len()]
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StateModel {
|
||||
variants: HashMap<String, Variants, BuildHasherDefault<FNVHash>>,
|
||||
}
|
||||
|
||||
impl StateModel {
|
||||
pub fn get_variants(&self, name: &str) -> Option<&Variants> {
|
||||
self.variants.get(name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Variants {
|
||||
models: Vec<Model>,
|
||||
}
|
||||
|
||||
impl Variants {
|
||||
fn choose_model<R: Rng>(&self, rng: &mut R) -> &Model {
|
||||
// TODO: Weighted random
|
||||
rng.choose(&self.models).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum BuiltinType {
|
||||
False,
|
||||
Generated,
|
||||
Entity,
|
||||
Compass,
|
||||
Clock
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RawModel {
|
||||
texture_vars: HashMap<String, String, BuildHasherDefault<FNVHash>>,
|
||||
elements: Vec<ModelElement>,
|
||||
ambient_occlusion: bool,
|
||||
ao_set: bool,
|
||||
|
||||
x: f64,
|
||||
y: f64,
|
||||
uvlock: bool,
|
||||
weight: f64,
|
||||
|
||||
display: HashMap<String, ModelDisplay, BuildHasherDefault<FNVHash>>,
|
||||
builtin: BuiltinType,
|
||||
}
|
||||
|
||||
impl RawModel {
|
||||
fn lookup_texture(&self, name: &str) -> String {
|
||||
if !name.is_empty() && name.starts_with('#') {
|
||||
let tex = self.texture_vars.get(&name[1..]).map(|v| v.clone()).unwrap_or("".to_owned());
|
||||
return self.lookup_texture(&tex);
|
||||
}
|
||||
name.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ModelDisplay {
|
||||
rotation: [f64; 3],
|
||||
translation: [f64; 3],
|
||||
scale: [f64; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ModelElement {
|
||||
from: [f64; 3],
|
||||
to: [f64; 3],
|
||||
shade: bool,
|
||||
rotation: Option<BlockRotation>,
|
||||
faces: [Option<BlockFace>; 6],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BlockRotation {
|
||||
origin: [f64; 3],
|
||||
axis: String,
|
||||
angle: f64,
|
||||
rescale: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BlockFace {
|
||||
uv: [f64; 4],
|
||||
texture: String,
|
||||
cull_face: Direction,
|
||||
rotation: i32,
|
||||
tint_index: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Model {
|
||||
faces: Vec<Face>,
|
||||
ambient_occlusion: bool,
|
||||
weight: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Face {
|
||||
cull_face: Direction,
|
||||
facing: Direction,
|
||||
vertices: Vec<chunk_builder::BlockVertex>,
|
||||
vertices_texture: Vec<render::Texture>,
|
||||
indices: usize,
|
||||
shade: bool,
|
||||
tint_index: i32,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn render<W: Write>(&self, snapshot: &world::Snapshot, x: i32, y: i32, z: i32, buf: &mut W) -> usize {
|
||||
let this = snapshot.get_block(x, y, z);
|
||||
let mut indices = 0;
|
||||
for face in &self.faces {
|
||||
if face.cull_face != Direction::Invalid {
|
||||
let (ox, oy, oz) = face.cull_face.get_offset();
|
||||
let other = snapshot.get_block(x + ox, y + oy, z + oz);
|
||||
if other.get_material().renderable || other == this { // TODO: cull check
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let (mut cr, mut cg, mut cb) = (255, 255, 255);
|
||||
if face.facing == Direction::West || face.facing == Direction::East {
|
||||
cr = ((cr as f64) * 0.8) as u8;
|
||||
cg = ((cg as f64) * 0.8) as u8;
|
||||
cb = ((cb as f64) * 0.8) as u8;
|
||||
}
|
||||
indices += face.indices;
|
||||
|
||||
for vert in &face.vertices {
|
||||
let mut vert = vert.clone();
|
||||
vert.r = cr;
|
||||
vert.g = cg;
|
||||
vert.b = cb;
|
||||
|
||||
vert.x += x as f32;
|
||||
vert.y += y as f32;
|
||||
vert.z += z as f32;
|
||||
|
||||
let (bl, sl) = calculate_light(
|
||||
&snapshot,
|
||||
x, y, z,
|
||||
vert.x as f64,
|
||||
vert.y as f64,
|
||||
vert.z as f64,
|
||||
face.facing,
|
||||
true,
|
||||
false
|
||||
);
|
||||
vert.block_light = bl;
|
||||
vert.sky_light = sl;
|
||||
vert.write(buf);
|
||||
}
|
||||
}
|
||||
indices
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn calculate_light(snapshot: &world::Snapshot, orig_x: i32, orig_y: i32, orig_z: i32,
|
||||
x: f64, y: f64, z: f64, face: Direction, smooth: bool, force: bool) -> (u16, u16) {
|
||||
use std::cmp::max;
|
||||
use world::block;
|
||||
let (ox, oy, oz) = if !snapshot.get_block(orig_x, orig_y, orig_z).get_material().renderable { // TODO: cull check
|
||||
(0, 0, 0)
|
||||
} else {
|
||||
face.get_offset()
|
||||
};
|
||||
|
||||
let s_block_light = snapshot.get_block_light(orig_x + ox, orig_y + oy, orig_z + oz);
|
||||
let s_sky_light = snapshot.get_sky_light(orig_x + ox, orig_y + oy, orig_z + oz);
|
||||
if !smooth {
|
||||
return ((s_block_light as u16) * 4000, (s_sky_light as u16) * 4000);
|
||||
}
|
||||
|
||||
let mut block_light = 0u32;
|
||||
let mut sky_light = 0u32;
|
||||
let mut count = 0;
|
||||
|
||||
let s_block_light = max(((s_block_light as i8) - 8), 0) as u8;
|
||||
let s_sky_light = max(((s_sky_light as i8) - 8), 0) as u8;
|
||||
|
||||
let dx = (ox as f64) * 0.6;
|
||||
let dy = (oy as f64) * 0.6;
|
||||
let dz = (oz as f64) * 0.6;
|
||||
|
||||
for ox in [-0.6, 0.0].into_iter() {
|
||||
for oy in [-0.6, 0.0].into_iter() {
|
||||
for oz in [-0.6, 0.0].into_iter() {
|
||||
let lx = (x + ox + dx).round() as i32;
|
||||
let ly = (y + oy + dy).round() as i32;
|
||||
let lz = (z + oz + dz).round() as i32;
|
||||
let mut bl = snapshot.get_block_light(lx, ly, lz);
|
||||
let mut sl = snapshot.get_sky_light(lx, ly, lz);
|
||||
if (force && match snapshot.get_block(lx, ly, lz) { block::Air{} => false, _ => true })
|
||||
|| (sl == 0 && bl == 0){
|
||||
bl = s_block_light;
|
||||
sl = s_sky_light;
|
||||
}
|
||||
block_light += bl as u32;
|
||||
sky_light += sl as u32;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((((block_light * 4000) / count) as u16), (((sky_light * 4000) / count) as u16))
|
||||
}
|
|
@ -31,6 +31,9 @@ use cgmath::{self, Vector, Point};
|
|||
use world;
|
||||
use collision;
|
||||
|
||||
use std::hash::BuildHasherDefault;
|
||||
use types::hash::FNVHash;
|
||||
|
||||
const ATLAS_SIZE: usize = 1024;
|
||||
|
||||
// TEMP
|
||||
|
@ -57,7 +60,7 @@ pub struct Renderer {
|
|||
chunk_shader_alpha: ChunkShaderAlpha,
|
||||
trans_shader: TransShader,
|
||||
|
||||
chunks: HashMap<(i32, i32, i32), ChunkBuffer>,
|
||||
chunks: HashMap<(i32, i32, i32), ChunkBuffer, BuildHasherDefault<FNVHash>>,
|
||||
element_buffer: gl::Buffer,
|
||||
element_buffer_size: usize,
|
||||
element_buffer_type: gl::Type,
|
||||
|
@ -188,7 +191,7 @@ impl Renderer {
|
|||
chunk_shader_alpha: chunk_shader_alpha,
|
||||
trans_shader: trans_shader,
|
||||
|
||||
chunks: HashMap::new(),
|
||||
chunks: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
element_buffer: gl::Buffer::new(),
|
||||
element_buffer_size: 0,
|
||||
element_buffer_type: gl::UNSIGNED_BYTE,
|
||||
|
@ -215,7 +218,7 @@ impl Renderer {
|
|||
let rm = self.resources.read().unwrap();
|
||||
if rm.version() != self.resource_version {
|
||||
self.resource_version = rm.version();
|
||||
println!("Updating textures to {}", self.resource_version);
|
||||
trace!("Updating textures to {}", self.resource_version);
|
||||
self.textures.write().unwrap().update_textures(self.resource_version);
|
||||
}
|
||||
}
|
||||
|
@ -656,7 +659,7 @@ impl TransInfo {
|
|||
}
|
||||
|
||||
pub struct TextureManager {
|
||||
textures: HashMap<String, Texture>,
|
||||
textures: HashMap<String, Texture, BuildHasherDefault<FNVHash>>,
|
||||
version: usize,
|
||||
resources: Arc<RwLock<resources::Manager>>,
|
||||
atlases: Vec<atlas::Atlas>,
|
||||
|
@ -664,21 +667,21 @@ pub struct TextureManager {
|
|||
animated_textures: Vec<AnimatedTexture>,
|
||||
pending_uploads: Vec<(i32, atlas::Rect, Vec<u8>)>,
|
||||
|
||||
dynamic_textures: HashMap<String, (i32, atlas::Rect)>,
|
||||
dynamic_textures: HashMap<String, (i32, atlas::Rect), BuildHasherDefault<FNVHash>>,
|
||||
free_dynamics: Vec<(i32, atlas::Rect)>,
|
||||
}
|
||||
|
||||
impl TextureManager {
|
||||
fn new(res: Arc<RwLock<resources::Manager>>) -> TextureManager {
|
||||
let mut tm = TextureManager {
|
||||
textures: HashMap::new(),
|
||||
textures: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
version: 0xFFFF,
|
||||
resources: res,
|
||||
atlases: Vec::new(),
|
||||
animated_textures: Vec::new(),
|
||||
pending_uploads: Vec::new(),
|
||||
|
||||
dynamic_textures: HashMap::new(),
|
||||
dynamic_textures: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
free_dynamics: Vec::new(),
|
||||
};
|
||||
tm.add_defaults();
|
||||
|
|
|
@ -17,7 +17,7 @@ extern crate byteorder;
|
|||
use std::fmt;
|
||||
use protocol::Serializable;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::Write;
|
||||
use self::byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
|
||||
use std::hash::Hasher;
|
||||
|
||||
pub struct FNVHash(u64);
|
||||
|
||||
impl Hasher for FNVHash {
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
for b in bytes {
|
||||
self.0 = self.0.wrapping_mul(0x100000001b3);
|
||||
self.0 ^= *b as u64
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FNVHash {
|
||||
fn default() -> Self {
|
||||
FNVHash(0xcbf29ce484222325)
|
||||
}
|
||||
}
|
|
@ -20,3 +20,4 @@ pub use self::metadata::*;
|
|||
|
||||
pub mod bit;
|
||||
pub mod nibble;
|
||||
pub mod hash;
|
||||
|
|
|
@ -33,6 +33,8 @@ macro_rules! define_blocks {
|
|||
},
|
||||
data $datafunc:expr,
|
||||
material $mat:expr,
|
||||
model $model:expr,
|
||||
variant $variant:expr,
|
||||
}
|
||||
)+
|
||||
) => (
|
||||
|
@ -137,6 +139,31 @@ macro_rules! define_blocks {
|
|||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_model(&self) -> (String, String) {
|
||||
match *self {
|
||||
$(
|
||||
Block::$name {
|
||||
$($fname,)*
|
||||
} => {
|
||||
let parts = $model;
|
||||
(String::from(parts.0), String::from(parts.1))
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_model_variant(&self) -> String {
|
||||
match *self {
|
||||
$(
|
||||
Block::$name {
|
||||
$($fname,)*
|
||||
} => {
|
||||
String::from($variant)
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
@ -159,8 +186,6 @@ macro_rules! define_blocks {
|
|||
|
||||
pub struct Material {
|
||||
pub renderable: bool,
|
||||
// TEMP
|
||||
pub texture: &'static str,
|
||||
}
|
||||
|
||||
define_blocks! {
|
||||
|
@ -169,8 +194,9 @@ define_blocks! {
|
|||
data { Some(0) },
|
||||
material Material {
|
||||
renderable: false,
|
||||
texture: "none",
|
||||
},
|
||||
model { ("minecraft", "air" ) },
|
||||
variant "normal",
|
||||
}
|
||||
Stone {
|
||||
props {
|
||||
|
@ -184,16 +210,9 @@ define_blocks! {
|
|||
data { Some(variant.data()) },
|
||||
material Material {
|
||||
renderable: true,
|
||||
texture: match variant {
|
||||
StoneVariant::Normal => "stone",
|
||||
StoneVariant::Granite => "stone_granite",
|
||||
StoneVariant::SmoothGranite => "stone_granite_smooth",
|
||||
StoneVariant::Diorite => "stone_diorite",
|
||||
StoneVariant::SmoothDiorite => "stone_diorite_smooth",
|
||||
StoneVariant::Andesite => "stone_andesite",
|
||||
StoneVariant::SmoothAndesite => "stone_andesite_smooth",
|
||||
},
|
||||
},
|
||||
model { ("minecraft", variant.as_string() ) },
|
||||
variant "normal",
|
||||
}
|
||||
Grass {
|
||||
props {
|
||||
|
@ -201,8 +220,9 @@ define_blocks! {
|
|||
data { Some(0) },
|
||||
material Material {
|
||||
renderable: true,
|
||||
texture: "grass_path_top",
|
||||
},
|
||||
model { ("minecraft", "grass" ) },
|
||||
variant "normal",
|
||||
}
|
||||
Dirt {
|
||||
props {
|
||||
|
@ -210,16 +230,18 @@ define_blocks! {
|
|||
data { Some(0) },
|
||||
material Material {
|
||||
renderable: true,
|
||||
texture: "dirt",
|
||||
},
|
||||
model { ("minecraft", "dirt" ) },
|
||||
variant "normal",
|
||||
}
|
||||
Missing {
|
||||
props {},
|
||||
data { None::<usize> },
|
||||
material Material {
|
||||
renderable: true,
|
||||
texture: "missing_texture",
|
||||
},
|
||||
model { ("steven", "missing_block" ) },
|
||||
variant "normal",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +259,13 @@ pub enum StoneVariant {
|
|||
|
||||
impl Display for StoneVariant {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
write!(f, "{}", match *self {
|
||||
write!(f, "{}", self.as_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl StoneVariant {
|
||||
fn as_string(&self) -> &'static str {
|
||||
match *self {
|
||||
StoneVariant::Normal => "stone",
|
||||
StoneVariant::Granite => "granite",
|
||||
StoneVariant::SmoothGranite => "smooth_granite",
|
||||
|
@ -245,11 +273,8 @@ impl Display for StoneVariant {
|
|||
StoneVariant::SmoothDiorite => "smooth_diorite",
|
||||
StoneVariant::Andesite => "andesite",
|
||||
StoneVariant::SmoothAndesite => "smooth_andesite",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StoneVariant {
|
||||
fn data(&self) -> usize {
|
||||
match *self {
|
||||
StoneVariant::Normal => 0,
|
||||
|
|
|
@ -16,19 +16,20 @@ pub mod block;
|
|||
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{BuildHasherDefault, Hasher};
|
||||
use std::hash::BuildHasherDefault;
|
||||
use types::bit;
|
||||
use types::nibble;
|
||||
use types::hash::FNVHash;
|
||||
use protocol;
|
||||
|
||||
pub struct World {
|
||||
chunks: HashMap<CPos, Chunk>,
|
||||
chunks: HashMap<CPos, Chunk, BuildHasherDefault<FNVHash>>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> World {
|
||||
World {
|
||||
chunks: HashMap::new(),
|
||||
chunks: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,27 +360,6 @@ impl Chunk {
|
|||
}
|
||||
}
|
||||
|
||||
struct FNVHash(u64);
|
||||
|
||||
impl Hasher for FNVHash {
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
for b in bytes {
|
||||
self.0 = self.0.wrapping_mul(0x100000001b3);
|
||||
self.0 ^= *b as u64
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FNVHash {
|
||||
fn default() -> Self {
|
||||
FNVHash(0xcbf29ce484222325)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct SectionKey {
|
||||
pos: (i32, u8, i32),
|
||||
|
|
Loading…
Reference in New Issue