Initial block model support

This commit is contained in:
Thinkofname 2016-03-24 15:39:57 +00:00
parent 73646421d3
commit 12b1dd6445
9 changed files with 925 additions and 170 deletions

View File

@ -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);

View File

@ -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,
};

737
src/model/mod.rs Normal file
View File

@ -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))
}

View File

@ -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();

View File

@ -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)]

24
src/types/hash.rs Normal file
View File

@ -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)
}
}

View File

@ -20,3 +20,4 @@ pub use self::metadata::*;
pub mod bit;
pub mod nibble;
pub mod hash;

View File

@ -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,

View File

@ -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),