2016-03-24 11:39:57 -04:00
|
|
|
|
2016-03-25 05:47:39 -04:00
|
|
|
pub mod liquid;
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
use std::collections::HashMap;
|
2016-03-31 09:59:40 -04:00
|
|
|
use std::cell::RefCell;
|
2016-03-24 11:39:57 -04:00
|
|
|
use std::io::Write;
|
2016-03-26 07:46:37 -04:00
|
|
|
use byteorder::{WriteBytesExt, NativeEndian};
|
2016-03-24 11:39:57 -04:00
|
|
|
use resources;
|
|
|
|
use render;
|
|
|
|
use world;
|
2016-03-31 09:59:40 -04:00
|
|
|
use world::block::{Block, TintType};
|
2016-04-03 15:53:40 -04:00
|
|
|
use shared::Direction;
|
2016-03-24 11:39:57 -04:00
|
|
|
use serde_json;
|
|
|
|
|
|
|
|
use std::hash::BuildHasherDefault;
|
|
|
|
use types::hash::FNVHash;
|
|
|
|
|
|
|
|
use rand::Rng;
|
2018-09-30 20:21:05 -04:00
|
|
|
use image::GenericImageView;
|
2016-03-24 11:39:57 -04:00
|
|
|
|
|
|
|
pub struct Factory {
|
|
|
|
resources: Arc<RwLock<resources::Manager>>,
|
2016-03-25 05:47:39 -04:00
|
|
|
pub textures: Arc<RwLock<render::TextureManager>>,
|
2016-03-24 11:39:57 -04:00
|
|
|
|
|
|
|
models: HashMap<Key, StateModel, BuildHasherDefault<FNVHash>>,
|
2016-03-24 15:39:14 -04:00
|
|
|
|
|
|
|
grass_colors: image::DynamicImage,
|
|
|
|
foliage_colors: image::DynamicImage,
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-03-31 09:59:40 -04:00
|
|
|
thread_local!(
|
|
|
|
static MULTIPART_CACHE: RefCell<HashMap<(Key, Block), Model, BuildHasherDefault<FNVHash>>> = RefCell::new(HashMap::with_hasher(BuildHasherDefault::default()))
|
|
|
|
);
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
impl Factory {
|
|
|
|
pub fn new(resources: Arc<RwLock<resources::Manager>>, textures: Arc<RwLock<render::TextureManager>>) -> Factory {
|
|
|
|
Factory {
|
2016-03-24 15:39:14 -04:00
|
|
|
grass_colors: Factory::load_biome_colors(resources.clone(), "grass"),
|
|
|
|
foliage_colors: Factory::load_biome_colors(resources.clone(), "foliage"),
|
2016-03-24 11:39:57 -04:00
|
|
|
resources: resources,
|
|
|
|
textures: textures,
|
|
|
|
|
|
|
|
models: HashMap::with_hasher(BuildHasherDefault::default()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-24 15:39:14 -04:00
|
|
|
fn load_biome_colors(res: Arc<RwLock<resources::Manager>>, name: &str) -> image::DynamicImage {
|
2016-03-24 20:18:46 -04:00
|
|
|
let mut val = match res.read().unwrap().open("minecraft", &format!("textures/colormap/{}.png", name)) {
|
|
|
|
Some(val) => val,
|
|
|
|
None => return image::DynamicImage::new_rgb8(256, 256),
|
|
|
|
};
|
2016-03-24 15:39:14 -04:00
|
|
|
let mut data = Vec::new();
|
|
|
|
val.read_to_end(&mut data).unwrap();
|
|
|
|
image::load_from_memory(&data).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn version_change(&mut self) {
|
2016-03-24 11:39:57 -04:00
|
|
|
self.models.clear();
|
2016-03-24 15:39:14 -04:00
|
|
|
self.grass_colors = Factory::load_biome_colors(self.resources.clone(), "grass");
|
|
|
|
self.foliage_colors = Factory::load_biome_colors(self.resources.clone(), "foliage");
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
2016-03-31 09:59:40 -04:00
|
|
|
fn get_model<R: Rng, W: Write>(&self, key: Key, block: Block, rng: &mut R, snapshot: &world::Snapshot, x: i32, y: i32, z: i32, buf: &mut W) -> Result<usize, bool> {
|
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
if let Some(model) = self.models.get(&key) {
|
|
|
|
if model.multipart.is_empty() {
|
|
|
|
let variant = block.get_model_variant();
|
|
|
|
if let Some(var) = model.get_variants(&variant) {
|
|
|
|
let model = var.choose_model(rng);
|
|
|
|
return Ok(model.render(self, snapshot, x, y, z, buf));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return MULTIPART_CACHE.with(|cache| {
|
|
|
|
let mut cache = cache.borrow_mut();
|
|
|
|
let entry = cache.entry((key.clone(), block));
|
|
|
|
match entry {
|
|
|
|
Entry::Occupied(e) => {
|
|
|
|
return Ok(e.get().render(self, snapshot, x, y, z, buf));
|
|
|
|
},
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
let mut res: Option<Model> = None;
|
|
|
|
for rule in &model.multipart {
|
|
|
|
let ok = Self::eval_rules(block, &rule.rules);
|
|
|
|
if ok {
|
|
|
|
if res.is_some() {
|
2016-09-15 10:15:52 -04:00
|
|
|
res.as_mut().unwrap().join(rule.apply.choose_model(rng));
|
2016-03-31 09:59:40 -04:00
|
|
|
} else {
|
|
|
|
res = Some(rule.apply.choose_model(rng).clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(mdl) = res {
|
|
|
|
return Ok(e.insert(mdl).render(self, snapshot, x, y, z, buf));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
2016-04-02 20:26:31 -04:00
|
|
|
Err(true)
|
2016-03-31 09:59:40 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
return Err(true);
|
|
|
|
}
|
|
|
|
Err(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn eval_rules(block: Block, rules: &[Rule]) -> bool {
|
|
|
|
for mrule in rules {
|
|
|
|
match *mrule {
|
|
|
|
Rule::Or(ref sub_rules) => {
|
|
|
|
let mut ok = false;
|
|
|
|
for srule in sub_rules {
|
|
|
|
if Self::eval_rules(block, srule) {
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Rule::Match(ref key, ref val) => {
|
2016-09-15 10:15:52 -04:00
|
|
|
if !block.match_multipart(key, val) {
|
2016-03-31 09:59:40 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_state_model<R: Rng, W: Write>(models: &Arc<RwLock<Factory>>, block: Block, rng: &mut R,
|
2016-03-24 11:39:57 -04:00
|
|
|
snapshot: &world::Snapshot, x: i32, y: i32, z: i32, buf: &mut W) -> usize {
|
2016-03-31 09:59:40 -04:00
|
|
|
let (plugin, name) = block.get_model();
|
2016-03-24 11:39:57 -04:00
|
|
|
let key = Key(plugin.to_owned(), name.to_owned());
|
2016-03-31 09:59:40 -04:00
|
|
|
let mut missing_variant;
|
2016-03-24 11:39:57 -04:00
|
|
|
{
|
|
|
|
let m = models.read().unwrap();
|
2016-03-31 09:59:40 -04:00
|
|
|
match m.get_model(key.clone(), block, rng, snapshot, x, y, z, buf) {
|
|
|
|
Ok(val) => return val,
|
|
|
|
Err(val) => missing_variant = val,
|
|
|
|
};
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
2016-03-29 09:52:07 -04:00
|
|
|
if !missing_variant {
|
|
|
|
// Whole model not loaded, try and load
|
2016-03-24 11:39:57 -04:00
|
|
|
let mut m = models.write().unwrap();
|
2016-03-31 09:59:40 -04:00
|
|
|
if !m.models.contains_key(&key) && !m.load_model(&plugin, &name) {
|
2016-03-26 10:24:26 -04:00
|
|
|
error!("Error loading model {}:{}", plugin, name);
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
2016-03-31 09:59:40 -04:00
|
|
|
match m.get_model(key.clone(), block, rng, snapshot, x, y, z, buf) {
|
|
|
|
Ok(val) => return val,
|
|
|
|
Err(val) => missing_variant = val,
|
|
|
|
};
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
2016-03-31 09:59:40 -04:00
|
|
|
let ret = Factory::get_state_model(models, Block::Missing{}, rng, snapshot, x, y, z, buf);
|
2016-03-29 09:52:07 -04:00
|
|
|
if !missing_variant {
|
|
|
|
// Still no model, replace with placeholder
|
2016-03-24 11:39:57 -04:00
|
|
|
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,
|
2016-03-29 09:52:07 -04:00
|
|
|
None => {
|
|
|
|
error!("Error missing block state for {}:{}", plugin, name);
|
|
|
|
return false;
|
|
|
|
},
|
2016-03-24 11:39:57 -04:00
|
|
|
};
|
|
|
|
let mdl: serde_json::Value = try_log!(serde_json::from_reader(file));
|
|
|
|
|
|
|
|
let mut model = StateModel {
|
|
|
|
variants: HashMap::with_hasher(BuildHasherDefault::default()),
|
2016-03-31 09:59:40 -04:00
|
|
|
multipart: vec![],
|
2016-03-24 11:39:57 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2016-03-31 09:59:40 -04:00
|
|
|
if let Some(multipart) = mdl.find("multipart").and_then(|v| v.as_array()) {
|
|
|
|
for rule in multipart {
|
|
|
|
let apply = self.parse_model_list(plugin, rule.find("apply").unwrap());
|
|
|
|
let mut rules = vec![];
|
|
|
|
if let Some(when) = rule.find("when").and_then(|v| v.as_object()) {
|
|
|
|
Self::parse_rules(when, &mut rules);
|
|
|
|
}
|
|
|
|
model.multipart.push(MultipartRule {
|
|
|
|
apply: apply,
|
|
|
|
rules: rules,
|
|
|
|
})
|
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
self.models.insert(Key(plugin.to_owned(), name.to_owned()), model);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2016-03-31 09:59:40 -04:00
|
|
|
fn parse_rules(when: &::std::collections::BTreeMap<String, serde_json::Value>, rules: &mut Vec<Rule>) {
|
|
|
|
for (name, val) in when {
|
|
|
|
if name == "OR" {
|
|
|
|
let mut or_rules = vec![];
|
|
|
|
for sub in val.as_array().unwrap() {
|
|
|
|
let mut sub_rules = vec![];
|
|
|
|
Self::parse_rules(sub.as_object().unwrap(), &mut sub_rules);
|
|
|
|
or_rules.push(sub_rules);
|
|
|
|
}
|
|
|
|
rules.push(Rule::Or(or_rules));
|
|
|
|
} else {
|
|
|
|
let v = match *val {
|
|
|
|
serde_json::Value::Bool(v) => v.to_string(),
|
|
|
|
serde_json::Value::I64(v) => v.to_string(),
|
|
|
|
serde_json::Value::U64(v) => v.to_string(),
|
|
|
|
serde_json::Value::F64(v) => v.to_string(),
|
|
|
|
serde_json::Value::String(ref v) => v.to_owned(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
rules.push(Rule::Match(name.to_owned(), v));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2016-09-15 10:15:52 -04:00
|
|
|
} else if let Some(mdl) = self.parse_block_state_variant(plugin, v) {
|
|
|
|
variants.models.push(self.process_model(mdl));
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
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));
|
2016-03-26 10:24:26 -04:00
|
|
|
match self.parse_model(plugin, &block_model) {
|
2016-03-24 11:39:57 -04:00
|
|
|
Some(val) => val,
|
|
|
|
None => {
|
|
|
|
error!("Failed to parse model {}", format!("models/{}.json", parent));
|
|
|
|
return None
|
|
|
|
},
|
2016-03-26 10:24:26 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
} 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 {
|
2016-03-26 10:24:26 -04:00
|
|
|
uv: face.find("uv").and_then(|v| v.as_array()).map_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
|
|
|
|
},
|
|
|
|
|v| [
|
|
|
|
v[0].as_f64().unwrap(),
|
|
|
|
v[1].as_f64().unwrap(),
|
|
|
|
v[2].as_f64().unwrap(),
|
|
|
|
v[3].as_f64().unwrap()
|
|
|
|
]
|
|
|
|
),
|
2016-03-24 11:39:57 -04:00
|
|
|
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())
|
2016-03-26 10:24:26 -04:00
|
|
|
.map_or(0, |v| v as i32),
|
2016-03-24 11:39:57 -04:00
|
|
|
tint_index: face.find("tintindex")
|
|
|
|
.and_then(|v| v.as_i64())
|
2016-03-26 10:24:26 -04:00
|
|
|
.map_or(-1, |v| v as i32),
|
2016-03-24 11:39:57 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(rotation) = v.find("rotation") {
|
|
|
|
element.rotation = Some(BlockRotation {
|
2016-03-26 10:24:26 -04:00
|
|
|
origin: rotation.find("origin").and_then(|v| v.as_array()).map_or([8.0, 8.0, 8.0], |v| [
|
2016-03-24 11:39:57 -04:00
|
|
|
v[0].as_f64().unwrap(),
|
|
|
|
v[1].as_f64().unwrap(),
|
|
|
|
v[2].as_f64().unwrap()
|
2016-03-26 10:24:26 -04:00
|
|
|
]),
|
2016-03-24 11:39:57 -04:00
|
|
|
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,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-03 15:53:40 -04:00
|
|
|
let mut verts = BlockVertex::face_by_direction(all_dirs[i]).to_vec();
|
2016-03-24 11:39:57 -04:00
|
|
|
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 {
|
2016-03-29 19:36:07 -04:00
|
|
|
let ox1 = ux1;
|
|
|
|
let ox2 = ux2;
|
|
|
|
let oy1 = uy1;
|
|
|
|
let oy2 = uy2;
|
2016-03-24 11:39:57 -04:00
|
|
|
match face.rotation {
|
2016-03-26 14:48:10 -04:00
|
|
|
270 => {
|
2016-03-29 19:36:07 -04:00
|
|
|
uy1 = tw*16 - ox2;
|
|
|
|
uy2 = tw*16 - ox1;
|
|
|
|
ux1 = oy1;
|
|
|
|
ux2 = oy2;
|
2016-03-24 11:39:57 -04:00
|
|
|
},
|
|
|
|
180 => {
|
2016-03-29 19:36:07 -04:00
|
|
|
uy1 = th*16 - oy2;
|
|
|
|
uy2 = th*16 - oy1;
|
|
|
|
ux1 = tw*16 - ox2;
|
|
|
|
ux2 = tw*16 - ox1;
|
2016-03-24 11:39:57 -04:00
|
|
|
},
|
2016-03-26 14:48:10 -04:00
|
|
|
90 => {
|
2016-03-29 19:36:07 -04:00
|
|
|
uy1 = ox1;
|
|
|
|
uy2 = ox2;
|
|
|
|
ux1 = th*16 - oy2;
|
|
|
|
ux2 = th*16 - oy1;
|
2016-03-24 11:39:57 -04:00
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2016-04-07 18:55:18 -04:00
|
|
|
let angle = r.angle * (::std::f64::consts::PI / 180.0);
|
|
|
|
let angle = if r.axis == "z" { angle } else { -angle } as f32;
|
|
|
|
let ci = 1.0 / angle.cos();
|
|
|
|
v.x -= (r.origin[0] / 16.0) as f32;
|
|
|
|
v.y -= (r.origin[1] / 16.0) as f32;
|
|
|
|
v.z -= (r.origin[2] / 16.0) as f32;
|
2016-03-24 14:27:43 -04:00
|
|
|
match &*r.axis {
|
|
|
|
"y" => {
|
2016-04-07 18:55:18 -04:00
|
|
|
let c = angle.cos();
|
|
|
|
let s = angle.sin();
|
|
|
|
let x = v.x;
|
|
|
|
let z = v.z;
|
|
|
|
v.x = x*c - z*s;
|
|
|
|
v.z = z*c + x*s;
|
|
|
|
|
|
|
|
if r.rescale {
|
2016-09-15 10:15:52 -04:00
|
|
|
v.x *= ci;
|
|
|
|
v.z *= ci;
|
2016-04-07 18:55:18 -04:00
|
|
|
}
|
2016-03-24 14:27:43 -04:00
|
|
|
},
|
|
|
|
"x" => {
|
2016-04-07 18:55:18 -04:00
|
|
|
let c = angle.cos();
|
|
|
|
let s = angle.sin();
|
|
|
|
let z = v.z;
|
|
|
|
let y = v.y;
|
|
|
|
v.z = z*c - y*s;
|
|
|
|
v.y = y*c + z*s;
|
|
|
|
|
|
|
|
if r.rescale {
|
2016-09-15 10:15:52 -04:00
|
|
|
v.z *= ci;
|
|
|
|
v.y *= ci;
|
2016-04-07 18:55:18 -04:00
|
|
|
}
|
2016-03-24 14:27:43 -04:00
|
|
|
},
|
|
|
|
"z" => {
|
2016-04-07 18:55:18 -04:00
|
|
|
let c = angle.cos();
|
|
|
|
let s = angle.sin();
|
|
|
|
let x = v.x;
|
|
|
|
let y = v.y;
|
|
|
|
v.x = x*c - y*s;
|
|
|
|
v.y = y*c + x*s;
|
|
|
|
|
|
|
|
if r.rescale {
|
2016-09-15 10:15:52 -04:00
|
|
|
v.x *= ci;
|
|
|
|
v.y *= ci;
|
2016-04-07 18:55:18 -04:00
|
|
|
}
|
2016-03-24 14:27:43 -04:00
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
2016-04-07 18:55:18 -04:00
|
|
|
v.x += (r.origin[0] / 16.0) as f32;
|
|
|
|
v.y += (r.origin[1] / 16.0) as f32;
|
|
|
|
v.z += (r.origin[2] / 16.0) as f32;
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if raw.x > 0.0 {
|
2016-03-24 14:27:43 -04:00
|
|
|
let rot_x = (raw.x * (::std::f64::consts::PI / 180.0)) as f32;
|
|
|
|
let c = rot_x.cos();
|
|
|
|
let s = rot_x.sin();
|
|
|
|
let z = v.z - 0.5;
|
|
|
|
let y = v.y - 0.5;
|
|
|
|
v.z = 0.5 + (z*c - y*s);
|
|
|
|
v.y = 0.5 + (y*c + z*s);
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2016-03-29 19:36:07 -04:00
|
|
|
let rot_y = (-face.rotation as f64 * (::std::f64::consts::PI / 180.0)) as f32;
|
2016-03-24 14:27:43 -04:00
|
|
|
let c = rot_y.cos() as i16;
|
|
|
|
let s = rot_y.sin() as i16;
|
|
|
|
let x = v.toffsetx - 8*tw;
|
|
|
|
let y = v.toffsety - 8*th;
|
|
|
|
v.toffsetx = 8*tw + (x*c - y*s);
|
|
|
|
v.toffsety = 8*th + (y*c + x*s);
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
2016-03-24 14:27:43 -04:00
|
|
|
if raw.uvlock && raw.y > 0.0
|
|
|
|
&& (processed_face.facing == Direction::Up || processed_face.facing == Direction::Down) {
|
|
|
|
let rot_y = (raw.y * (::std::f64::consts::PI / 180.0)) as f32;
|
|
|
|
let c = rot_y.cos() as i16;
|
|
|
|
let s = rot_y.sin() as i16;
|
2016-03-29 19:36:07 -04:00
|
|
|
let x = v.toffsetx - 8*tw;
|
|
|
|
let y = v.toffsety - 8*th;
|
|
|
|
v.toffsetx = 8*tw + (x*c - y*s);
|
|
|
|
v.toffsety = 8*th + (y*c + x*s);
|
2016-03-24 14:27:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if raw.uvlock && raw.x > 0.0
|
|
|
|
&& (processed_face.facing != Direction::Up && processed_face.facing != Direction::Down) {
|
|
|
|
let rot_x = (raw.x * (::std::f64::consts::PI / 180.0)) as f32;
|
|
|
|
let c = rot_x.cos() as i16;
|
|
|
|
let s = rot_x.sin() as i16;
|
2016-03-29 19:36:07 -04:00
|
|
|
let x = v.toffsetx - 8*tw;
|
|
|
|
let y = v.toffsety - 8*th;
|
|
|
|
v.toffsetx = 8*tw + (x*c - y*s);
|
|
|
|
v.toffsety = 8*th + (y*c + x*s);
|
2016-03-24 14:27:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
processed_face.vertices = verts;
|
|
|
|
processed_face.indices = 6;
|
|
|
|
model.faces.push(processed_face);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
model
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const FACE_ROTATION: &'static [Direction] = &[
|
2016-03-29 19:36:07 -04:00
|
|
|
Direction::North,
|
|
|
|
Direction::East,
|
|
|
|
Direction::South,
|
|
|
|
Direction::West,
|
2016-03-24 11:39:57 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
const FACE_ROTATION_X: &'static [Direction] = &[
|
2016-03-29 19:36:07 -04:00
|
|
|
Direction::North,
|
|
|
|
Direction::Down,
|
|
|
|
Direction::South,
|
|
|
|
Direction::Up,
|
2016-03-24 11:39:57 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
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)
|
2016-03-29 08:44:46 -04:00
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
rots[(rots.len() as i32 + pos + offset) as usize % rots.len()]
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct StateModel {
|
|
|
|
variants: HashMap<String, Variants, BuildHasherDefault<FNVHash>>,
|
2016-03-31 09:59:40 -04:00
|
|
|
multipart: Vec<MultipartRule>,
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl StateModel {
|
|
|
|
pub fn get_variants(&self, name: &str) -> Option<&Variants> {
|
|
|
|
self.variants.get(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-31 09:59:40 -04:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct MultipartRule {
|
|
|
|
apply: Variants,
|
|
|
|
rules: Vec<Rule>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
enum Rule {
|
|
|
|
Match(String, String),
|
|
|
|
Or(Vec<Vec<Rule>>),
|
|
|
|
}
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
#[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('#') {
|
2016-03-26 10:24:26 -04:00
|
|
|
let tex = self.texture_vars.get(&name[1..]).cloned().unwrap_or("".to_owned());
|
2016-03-24 11:39:57 -04:00
|
|
|
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,
|
2016-03-26 07:46:37 -04:00
|
|
|
vertices: Vec<BlockVertex>,
|
2016-03-24 11:39:57 -04:00
|
|
|
vertices_texture: Vec<render::Texture>,
|
|
|
|
indices: usize,
|
|
|
|
shade: bool,
|
|
|
|
tint_index: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Model {
|
2016-03-31 09:59:40 -04:00
|
|
|
fn join(&mut self, other: &Model) {
|
|
|
|
self.faces.extend_from_slice(&other.faces);
|
|
|
|
}
|
|
|
|
|
2016-03-24 15:39:14 -04:00
|
|
|
fn render<W: Write>(&self, factory: &Factory, snapshot: &world::Snapshot, x: i32, y: i32, z: i32, buf: &mut W) -> usize {
|
2016-03-24 11:39:57 -04:00
|
|
|
let this = snapshot.get_block(x, y, z);
|
2016-03-24 14:09:28 -04:00
|
|
|
let this_mat = this.get_material();
|
2016-03-24 11:39:57 -04:00
|
|
|
let mut indices = 0;
|
2016-03-24 15:20:26 -04:00
|
|
|
|
|
|
|
let tint = this.get_tint();
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
for face in &self.faces {
|
2016-03-25 17:15:13 -04:00
|
|
|
if face.cull_face != Direction::Invalid && !this_mat.never_cull {
|
2016-03-24 11:39:57 -04:00
|
|
|
let (ox, oy, oz) = face.cull_face.get_offset();
|
|
|
|
let other = snapshot.get_block(x + ox, y + oy, z + oz);
|
2016-03-24 14:09:28 -04:00
|
|
|
if other.get_material().should_cull_against || other == this {
|
2016-03-24 11:39:57 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
indices += face.indices;
|
|
|
|
|
|
|
|
for vert in &face.vertices {
|
|
|
|
let mut vert = vert.clone();
|
|
|
|
|
|
|
|
vert.x += x as f32;
|
|
|
|
vert.y += y as f32;
|
|
|
|
vert.z += z as f32;
|
|
|
|
|
2016-04-04 10:05:24 -04:00
|
|
|
let (mut cr, mut cg, mut cb) = if face.tint_index == 0 {
|
|
|
|
match tint {
|
|
|
|
TintType::Default => (255, 255, 255),
|
|
|
|
TintType::Color{r, g, b} => (r, g, b),
|
|
|
|
TintType::Grass => calculate_biome(snapshot, vert.x as i32, vert.z as i32, &factory.grass_colors),
|
|
|
|
TintType::Foliage => calculate_biome(snapshot, vert.x as i32, vert.z as i32, &factory.foliage_colors),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(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;
|
|
|
|
}
|
|
|
|
|
|
|
|
vert.r = cr;
|
|
|
|
vert.g = cg;
|
|
|
|
vert.b = cb;
|
|
|
|
|
2016-03-24 11:39:57 -04:00
|
|
|
let (bl, sl) = calculate_light(
|
2016-09-15 10:15:52 -04:00
|
|
|
snapshot,
|
2016-03-24 11:39:57 -04:00
|
|
|
x, y, z,
|
|
|
|
vert.x as f64,
|
|
|
|
vert.y as f64,
|
|
|
|
vert.z as f64,
|
|
|
|
face.facing,
|
2016-03-24 14:09:28 -04:00
|
|
|
self.ambient_occlusion,
|
|
|
|
this_mat.force_shade
|
2016-03-24 11:39:57 -04:00
|
|
|
);
|
|
|
|
vert.block_light = bl;
|
|
|
|
vert.sky_light = sl;
|
|
|
|
vert.write(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
indices
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-24 15:39:14 -04:00
|
|
|
fn calculate_biome(snapshot: &world::Snapshot, x: i32, z: i32, img: &image::DynamicImage) -> (u8, u8, u8) {
|
2016-03-25 10:17:54 -04:00
|
|
|
use std::cmp::{min, max};
|
2016-03-24 15:39:14 -04:00
|
|
|
let mut count = 0;
|
|
|
|
let mut r = 0;
|
|
|
|
let mut g = 0;
|
|
|
|
let mut b = 0;
|
2016-04-04 10:05:24 -04:00
|
|
|
for xx in -1 .. 2 {
|
|
|
|
for zz in -1 .. 2 {
|
2016-03-24 15:39:14 -04:00
|
|
|
let bi = snapshot.get_biome(x+xx, z+zz);
|
2016-04-24 15:09:24 -04:00
|
|
|
let color_index = bi.get_color_index();
|
|
|
|
let ix = color_index & 0xFF;
|
|
|
|
let iy = color_index >> 8;
|
2016-03-24 15:39:14 -04:00
|
|
|
|
2016-03-25 10:17:54 -04:00
|
|
|
let ix = min(max(ix, 0), 255);
|
|
|
|
let iy = min(max(iy, 0), 255);
|
|
|
|
|
2016-03-24 15:39:14 -04:00
|
|
|
let col = img.get_pixel(ix as u32, iy as u32);
|
2016-04-24 15:09:24 -04:00
|
|
|
let col = bi.process_color(col);
|
2016-03-24 15:39:14 -04:00
|
|
|
r += col.data[0] as u32;
|
|
|
|
g += col.data[1] as u32;
|
|
|
|
b += col.data[2] as u32;
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
((r/count) as u8, (g/count) as u8, (b/count) as u8)
|
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
|
|
|
|
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;
|
2016-03-27 18:57:35 -04:00
|
|
|
let (ox, oy, oz) = face.get_offset();
|
2016-03-24 11:39:57 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-09-30 02:28:38 -04:00
|
|
|
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;
|
2016-03-24 11:39:57 -04:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2016-03-26 07:46:37 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub const PRECOMPUTED_VERTS: [&'static [BlockVertex; 4]; 6] = [
|
|
|
|
&[ // Up
|
|
|
|
BlockVertex::base(0.0, 1.0, 0.0, 0, 0),
|
|
|
|
BlockVertex::base(1.0, 1.0, 0.0, 1, 0),
|
|
|
|
BlockVertex::base(0.0, 1.0, 1.0, 0, 1),
|
|
|
|
BlockVertex::base(1.0, 1.0, 1.0, 1, 1),
|
|
|
|
],
|
|
|
|
&[ // Down
|
|
|
|
BlockVertex::base(0.0, 0.0, 0.0, 0, 1),
|
|
|
|
BlockVertex::base(0.0, 0.0, 1.0, 0, 0),
|
|
|
|
BlockVertex::base(1.0, 0.0, 0.0, 1, 1),
|
|
|
|
BlockVertex::base(1.0, 0.0, 1.0, 1, 0),
|
|
|
|
],
|
|
|
|
&[ // North
|
|
|
|
BlockVertex::base(0.0, 0.0, 0.0, 1, 1),
|
|
|
|
BlockVertex::base(1.0, 0.0, 0.0, 0, 1),
|
|
|
|
BlockVertex::base(0.0, 1.0, 0.0, 1, 0),
|
|
|
|
BlockVertex::base(1.0, 1.0, 0.0, 0, 0),
|
|
|
|
],
|
|
|
|
&[ // South
|
|
|
|
BlockVertex::base(0.0, 0.0, 1.0, 0, 1),
|
|
|
|
BlockVertex::base(0.0, 1.0, 1.0, 0, 0),
|
|
|
|
BlockVertex::base(1.0, 0.0, 1.0, 1, 1),
|
|
|
|
BlockVertex::base(1.0, 1.0, 1.0, 1, 0),
|
|
|
|
],
|
|
|
|
&[ // West
|
|
|
|
BlockVertex::base(0.0, 0.0, 0.0, 0, 1),
|
|
|
|
BlockVertex::base(0.0, 1.0, 0.0, 0, 0),
|
|
|
|
BlockVertex::base(0.0, 0.0, 1.0, 1, 1),
|
|
|
|
BlockVertex::base(0.0, 1.0, 1.0, 1, 0),
|
|
|
|
],
|
|
|
|
&[ // East
|
|
|
|
BlockVertex::base(1.0, 0.0, 0.0, 1, 1),
|
|
|
|
BlockVertex::base(1.0, 0.0, 1.0, 0, 1),
|
|
|
|
BlockVertex::base(1.0, 1.0, 0.0, 1, 0),
|
|
|
|
BlockVertex::base(1.0, 1.0, 1.0, 0, 0),
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct BlockVertex {
|
|
|
|
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 {
|
|
|
|
const fn base(x: f32, y: f32, z: f32, tx: i16, ty: i16) -> BlockVertex {
|
|
|
|
BlockVertex {
|
|
|
|
x: x, y: y, z: z,
|
|
|
|
tx: 0, ty: 0, tw: 0, th: 0,
|
|
|
|
toffsetx: tx, toffsety: ty, tatlas: 0,
|
|
|
|
r: 0, g: 0, b: 0,
|
|
|
|
block_light: 0, sky_light: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(self.tx);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(self.ty);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(self.tw);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(self.th);
|
|
|
|
let _ = w.write_i16::<NativeEndian>(self.toffsetx);
|
|
|
|
let _ = w.write_i16::<NativeEndian>(self.toffsety);
|
|
|
|
let _ = w.write_i16::<NativeEndian>(self.tatlas);
|
|
|
|
let _ = w.write_i16::<NativeEndian>(0);
|
|
|
|
let _ = w.write_u8(self.r);
|
|
|
|
let _ = w.write_u8(self.g);
|
|
|
|
let _ = w.write_u8(self.b);
|
|
|
|
let _ = w.write_u8(255);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(self.block_light);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(self.sky_light);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(0);
|
|
|
|
let _ = w.write_u16::<NativeEndian>(0);
|
|
|
|
}
|
2016-04-03 15:53:40 -04:00
|
|
|
|
|
|
|
pub fn face_by_direction(dir: Direction) -> &'static [BlockVertex; 4] {
|
|
|
|
match dir {
|
|
|
|
Direction::Up => PRECOMPUTED_VERTS[0],
|
|
|
|
Direction::Down => PRECOMPUTED_VERTS[1],
|
|
|
|
Direction::North => PRECOMPUTED_VERTS[2],
|
|
|
|
Direction::South => PRECOMPUTED_VERTS[3],
|
|
|
|
Direction::West => PRECOMPUTED_VERTS[4],
|
|
|
|
Direction::East => PRECOMPUTED_VERTS[5],
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
2016-03-26 07:46:37 -04:00
|
|
|
}
|