2016-03-25 05:47:39 -04:00
|
|
|
pub mod liquid;
|
|
|
|
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::render;
|
2020-06-21 15:17:24 -04:00
|
|
|
use crate::resources;
|
|
|
|
use crate::shared::Direction;
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::world;
|
|
|
|
use crate::world::block::{Block, TintType};
|
2020-06-21 15:17:24 -04:00
|
|
|
use byteorder::{NativeEndian, WriteBytesExt};
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::io::Write;
|
|
|
|
use std::sync::{Arc, RwLock};
|
2016-03-24 11:39:57 -04:00
|
|
|
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::types::hash::FNVHash;
|
2020-06-21 15:17:24 -04:00
|
|
|
use log::error;
|
|
|
|
use std::hash::BuildHasherDefault;
|
2016-03-24 11:39:57 -04:00
|
|
|
|
2018-09-30 20:21:05 -04:00
|
|
|
use image::GenericImageView;
|
2020-06-21 15:17:24 -04:00
|
|
|
use rand::seq::SliceRandom;
|
|
|
|
use rand::Rng;
|
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 {
|
2020-06-21 15:17:24 -04:00
|
|
|
($e:expr) => {
|
2016-03-24 11:39:57 -04:00
|
|
|
match $e {
|
|
|
|
Ok(val) => val,
|
|
|
|
Err(err) => {
|
|
|
|
error!("Error loading model {:?}", err);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
};
|
|
|
|
(opt $e:expr) => {
|
2016-03-24 11:39:57 -04:00
|
|
|
match $e {
|
|
|
|
Ok(val) => val,
|
|
|
|
Err(err) => {
|
|
|
|
error!("Error loading model {:?}", err);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
};
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
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 {
|
2020-06-21 15:17:24 -04:00
|
|
|
pub fn new(
|
|
|
|
resources: Arc<RwLock<resources::Manager>>,
|
|
|
|
textures: Arc<RwLock<render::TextureManager>>,
|
|
|
|
) -> Factory {
|
2016-03-24 11:39:57 -04:00
|
|
|
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"),
|
2018-11-04 16:43:30 -05:00
|
|
|
resources,
|
|
|
|
textures,
|
2016-03-24 11:39:57 -04:00
|
|
|
|
|
|
|
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 {
|
2020-06-21 15:17:24 -04:00
|
|
|
let mut val = match res
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.open("minecraft", &format!("textures/colormap/{}.png", name))
|
|
|
|
{
|
2016-03-24 20:18:46 -04:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -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> {
|
2016-03-31 09:59:40 -04:00
|
|
|
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));
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-31 09:59:40 -04:00
|
|
|
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));
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-31 09:59:40 -04:00
|
|
|
};
|
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;
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-31 09:59:40 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
pub fn get_state_model<R: Rng, W: Write>(
|
|
|
|
models: &Arc<RwLock<Factory>>,
|
|
|
|
block: Block,
|
|
|
|
rng: &mut R,
|
|
|
|
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
|
|
|
}
|
2020-06-21 15:17:24 -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();
|
2020-06-21 15:17:24 -04:00
|
|
|
let model = m
|
|
|
|
.models
|
|
|
|
.get(&Key("steven".to_owned(), "missing_block".to_owned()))
|
|
|
|
.unwrap()
|
|
|
|
.clone();
|
2016-03-24 11:39:57 -04:00
|
|
|
m.models.insert(key, model);
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_model(&mut self, plugin: &str, name: &str) -> bool {
|
2020-06-21 15:17:24 -04:00
|
|
|
let file = match self
|
|
|
|
.resources
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.open(plugin, &format!("blockstates/{}.json", name))
|
|
|
|
{
|
2016-03-24 11:39:57 -04:00
|
|
|
Some(val) => val,
|
2016-03-29 09:52:07 -04:00
|
|
|
None => {
|
|
|
|
error!("Error missing block state for {}:{}", plugin, name);
|
|
|
|
return false;
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
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
|
|
|
};
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(variants) = mdl.get("variants").and_then(|v| v.as_object()) {
|
2016-03-24 11:39:57 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(multipart) = mdl.get("multipart").and_then(|v| v.as_array()) {
|
2016-03-31 09:59:40 -04:00
|
|
|
for rule in multipart {
|
2018-10-23 21:47:21 -04:00
|
|
|
let apply = self.parse_model_list(plugin, rule.get("apply").unwrap());
|
2016-03-31 09:59:40 -04:00
|
|
|
let mut rules = vec![];
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(when) = rule.get("when").and_then(|v| v.as_object()) {
|
2016-03-31 09:59:40 -04:00
|
|
|
Self::parse_rules(when, &mut rules);
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
model.multipart.push(MultipartRule { apply, rules })
|
2016-03-31 09:59:40 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
self.models
|
|
|
|
.insert(Key(plugin.to_owned(), name.to_owned()), model);
|
2016-03-24 11:39:57 -04:00
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
fn parse_rules(when: &serde_json::Map<String, serde_json::Value>, rules: &mut Vec<Rule>) {
|
2016-03-31 09:59:40 -04:00
|
|
|
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 {
|
2018-10-23 21:47:21 -04:00
|
|
|
serde_json::Value::Bool(ref v) => v.to_string(),
|
|
|
|
serde_json::Value::Number(ref v) => v.to_string(),
|
2016-03-31 09:59:40 -04:00
|
|
|
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 {
|
2020-06-21 15:17:24 -04:00
|
|
|
let mut variants = Variants { models: vec![] };
|
2016-03-24 11:39:57 -04:00
|
|
|
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> {
|
2018-10-23 21:47:21 -04:00
|
|
|
let model_name = match v.get("model").and_then(|v| v.as_str()) {
|
2016-03-24 11:39:57 -04:00
|
|
|
Some(val) => val,
|
|
|
|
None => {
|
|
|
|
error!("Couldn't find model name");
|
|
|
|
return None;
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
};
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
let file = match self
|
|
|
|
.resources
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.open(plugin, &format!("models/block/{}.json", model_name))
|
|
|
|
{
|
2016-03-24 11:39:57 -04:00
|
|
|
Some(val) => val,
|
|
|
|
None => {
|
2020-06-21 15:17:24 -04:00
|
|
|
error!(
|
|
|
|
"Couldn't find model {}",
|
|
|
|
format!("models/block/{}.json", model_name)
|
|
|
|
);
|
2016-03-24 11:39:57 -04:00
|
|
|
return None;
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
};
|
|
|
|
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 => {
|
2020-06-21 15:17:24 -04:00
|
|
|
error!(
|
|
|
|
"Failed to parse model {}",
|
|
|
|
format!("models/block/{}.json", model_name)
|
|
|
|
);
|
2016-03-24 11:39:57 -04:00
|
|
|
return None;
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
};
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
model.y = v.get("y").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
|
|
|
model.x = v.get("x").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
|
|
|
model.uvlock = v.get("uvlock").and_then(|v| v.as_bool()).unwrap_or(false);
|
|
|
|
model.weight = v.get("weight").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
2016-03-24 11:39:57 -04:00
|
|
|
Some(model)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_model(&self, plugin: &str, v: &serde_json::Value) -> Option<RawModel> {
|
2018-10-23 21:47:21 -04:00
|
|
|
let parent = v.get("parent").and_then(|v| v.as_str()).unwrap_or("");
|
2016-03-24 11:39:57 -04:00
|
|
|
let mut model = if !parent.is_empty() && !parent.starts_with("builtin/") {
|
2020-06-21 15:17:24 -04:00
|
|
|
let file = match self
|
|
|
|
.resources
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.open(plugin, &format!("models/{}.json", parent))
|
|
|
|
{
|
2016-03-24 11:39:57 -04:00
|
|
|
Some(val) => val,
|
|
|
|
None => {
|
|
|
|
error!("Couldn't find model {}", format!("models/{}.json", parent));
|
|
|
|
return None;
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
};
|
|
|
|
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 => {
|
2020-06-21 15:17:24 -04:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(textures) = v.get("textures").and_then(|v| v.as_object()) {
|
2016-03-24 11:39:57 -04:00
|
|
|
for (k, v) in textures {
|
2020-06-21 15:17:24 -04:00
|
|
|
model
|
|
|
|
.texture_vars
|
|
|
|
.insert(k.clone(), v.as_str().unwrap_or("").to_owned());
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(ao) = v.get("ambientocclusion").and_then(|v| v.as_bool()) {
|
2016-03-24 11:39:57 -04:00
|
|
|
model.ambient_occlusion = ao;
|
|
|
|
model.ao_set = true;
|
|
|
|
} else if !model.ao_set {
|
|
|
|
model.ambient_occlusion = true;
|
|
|
|
}
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(elements) = v.get("elements").and_then(|v| v.as_array()) {
|
2016-03-24 11:39:57 -04:00
|
|
|
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 {
|
2020-06-21 15:17:24 -04:00
|
|
|
from: v
|
|
|
|
.get("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
|
|
|
|
.get("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(),
|
2018-10-23 21:47:21 -04:00
|
|
|
shade: v.get("shade").and_then(|v| v.as_bool()).unwrap_or(false),
|
2016-03-24 11:39:57 -04:00
|
|
|
faces: [None, None, None, None, None, None],
|
|
|
|
rotation: None,
|
|
|
|
};
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(faces) = v.get("faces").and_then(|v| v.as_object()) {
|
2016-03-24 11:39:57 -04:00
|
|
|
for dir in Direction::all() {
|
|
|
|
if let Some(face) = faces.get(dir.as_string()) {
|
|
|
|
element.faces[dir.index()] = Some(BlockFace {
|
2018-10-23 21:47:21 -04:00
|
|
|
uv: face.get("uv").and_then(|v| v.as_array()).map_or_else(
|
2016-03-26 10:24:26 -04:00
|
|
|
|| {
|
|
|
|
let mut uv = [0.0, 0.0, 16.0, 16.0];
|
|
|
|
match dir {
|
|
|
|
Direction::North | Direction::South => {
|
2020-06-21 15:17:24 -04:00
|
|
|
uv[0] = element.from[0];
|
|
|
|
uv[2] = element.to[0];
|
|
|
|
uv[1] = 16.0 - element.to[1];
|
|
|
|
uv[3] = 16.0 - element.from[1];
|
|
|
|
}
|
2016-03-26 10:24:26 -04:00
|
|
|
Direction::West | Direction::East => {
|
2020-06-21 15:17:24 -04:00
|
|
|
uv[0] = element.from[2];
|
|
|
|
uv[2] = element.to[2];
|
|
|
|
uv[1] = 16.0 - element.to[1];
|
|
|
|
uv[3] = 16.0 - element.from[1];
|
|
|
|
}
|
2016-03-26 10:24:26 -04:00
|
|
|
Direction::Down | Direction::Up => {
|
2020-06-21 15:17:24 -04:00
|
|
|
uv[0] = element.from[0];
|
|
|
|
uv[2] = element.to[0];
|
|
|
|
uv[1] = 16.0 - element.to[2];
|
|
|
|
uv[3] = 16.0 - element.from[2];
|
|
|
|
}
|
2016-03-26 10:24:26 -04:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
uv
|
|
|
|
},
|
2020-06-21 15:17:24 -04:00
|
|
|
|v| {
|
|
|
|
[
|
|
|
|
v[0].as_f64().unwrap(),
|
|
|
|
v[1].as_f64().unwrap(),
|
|
|
|
v[2].as_f64().unwrap(),
|
|
|
|
v[3].as_f64().unwrap(),
|
|
|
|
]
|
|
|
|
},
|
2016-03-26 10:24:26 -04:00
|
|
|
),
|
2020-06-21 15:17:24 -04:00
|
|
|
texture: face
|
|
|
|
.get("texture")
|
2018-10-23 21:47:21 -04:00
|
|
|
.and_then(|v| v.as_str())
|
2020-06-21 15:17:24 -04:00
|
|
|
.map(|v| {
|
|
|
|
if v.starts_with('#') {
|
|
|
|
v.to_owned()
|
|
|
|
} else {
|
|
|
|
"#".to_owned() + v
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap(),
|
2016-03-24 11:39:57 -04:00
|
|
|
cull_face: Direction::from_string(
|
2020-06-21 15:17:24 -04:00
|
|
|
face.get("cullface")
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
.unwrap_or("invalid"),
|
|
|
|
),
|
|
|
|
rotation: face
|
|
|
|
.get("rotation")
|
2016-03-24 11:39:57 -04:00
|
|
|
.and_then(|v| v.as_i64())
|
2016-03-26 10:24:26 -04:00
|
|
|
.map_or(0, |v| v as i32),
|
2020-06-21 15:17:24 -04:00
|
|
|
tint_index: face
|
|
|
|
.get("tintindex")
|
2016-03-24 11:39:57 -04:00
|
|
|
.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
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 21:47:21 -04:00
|
|
|
if let Some(rotation) = v.get("rotation") {
|
2016-03-24 11:39:57 -04:00
|
|
|
element.rotation = Some(BlockRotation {
|
2020-06-21 15:17:24 -04:00
|
|
|
origin: rotation.get("origin").and_then(|v| v.as_array()).map_or(
|
|
|
|
[8.0, 8.0, 8.0],
|
|
|
|
|v| {
|
|
|
|
[
|
|
|
|
v[0].as_f64().unwrap(),
|
|
|
|
v[1].as_f64().unwrap(),
|
|
|
|
v[2].as_f64().unwrap(),
|
|
|
|
]
|
|
|
|
},
|
|
|
|
),
|
|
|
|
axis: rotation
|
|
|
|
.get("axis")
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
.unwrap_or("")
|
|
|
|
.to_owned(),
|
|
|
|
angle: rotation
|
|
|
|
.get("angle")
|
|
|
|
.and_then(|v| v.as_f64())
|
|
|
|
.unwrap_or(0.0),
|
|
|
|
rescale: rotation
|
|
|
|
.get("rescale")
|
|
|
|
.and_then(|v| v.as_bool())
|
|
|
|
.unwrap_or(false),
|
2016-03-24 11:39:57 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
element
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_model(&self, mut raw: RawModel) -> Model {
|
|
|
|
let mut model = Model {
|
|
|
|
faces: vec![],
|
|
|
|
ambient_occlusion: raw.ambient_occlusion,
|
|
|
|
weight: raw.weight,
|
|
|
|
};
|
2021-06-18 17:58:36 -04:00
|
|
|
let elements = std::mem::take(&mut raw.elements);
|
2016-03-24 11:39:57 -04:00
|
|
|
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;
|
2020-06-21 15:17:24 -04:00
|
|
|
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],
|
|
|
|
);
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
if raw.y > 0.0 {
|
|
|
|
let o = (raw.y as i32) / 90;
|
2020-06-21 15:17:24 -04:00
|
|
|
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-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
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 => {
|
2020-06-21 15:17:24 -04:00
|
|
|
uy1 = tw * 16 - ox2;
|
|
|
|
uy2 = tw * 16 - ox1;
|
2016-03-29 19:36:07 -04:00
|
|
|
ux1 = oy1;
|
|
|
|
ux2 = oy2;
|
2020-06-21 15:17:24 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
180 => {
|
2020-06-21 15:17:24 -04:00
|
|
|
uy1 = th * 16 - oy2;
|
|
|
|
uy2 = th * 16 - oy1;
|
|
|
|
ux1 = tw * 16 - ox2;
|
|
|
|
ux2 = tw * 16 - ox1;
|
|
|
|
}
|
2016-03-26 14:48:10 -04:00
|
|
|
90 => {
|
2016-03-29 19:36:07 -04:00
|
|
|
uy1 = ox1;
|
|
|
|
uy2 = ox2;
|
2020-06-21 15:17:24 -04:00
|
|
|
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;
|
2020-06-21 15:17:24 -04:00
|
|
|
v.x = x * c - z * s;
|
|
|
|
v.z = z * c + x * s;
|
2016-04-07 18:55:18 -04:00
|
|
|
|
|
|
|
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
|
|
|
}
|
2020-06-21 15:17:24 -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;
|
2020-06-21 15:17:24 -04:00
|
|
|
v.z = z * c - y * s;
|
|
|
|
v.y = y * c + z * s;
|
2016-04-07 18:55:18 -04:00
|
|
|
|
|
|
|
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
|
|
|
}
|
2020-06-21 15:17:24 -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;
|
2020-06-21 15:17:24 -04:00
|
|
|
v.x = x * c - y * s;
|
|
|
|
v.y = y * c + x * s;
|
2016-04-07 18:55:18 -04:00
|
|
|
|
|
|
|
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
|
|
|
}
|
2020-06-21 15:17:24 -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;
|
2020-06-21 15:17:24 -04:00
|
|
|
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;
|
2020-06-21 15:17:24 -04:00
|
|
|
v.x = 0.5 + (x * c - z * s);
|
|
|
|
v.z = 0.5 + (z * c + x * s);
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-06-21 15:17:24 -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;
|
2020-06-21 15:17:24 -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 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
if raw.uvlock
|
|
|
|
&& raw.y > 0.0
|
|
|
|
&& (processed_face.facing == Direction::Up
|
|
|
|
|| processed_face.facing == Direction::Down)
|
|
|
|
{
|
2016-03-24 14:27:43 -04:00
|
|
|
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;
|
2020-06-21 15:17:24 -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
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
if raw.uvlock
|
|
|
|
&& raw.x > 0.0
|
|
|
|
&& (processed_face.facing != Direction::Up
|
|
|
|
&& processed_face.facing != Direction::Down)
|
|
|
|
{
|
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() as i16;
|
|
|
|
let s = rot_x.sin() as i16;
|
2020-06-21 15:17:24 -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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 16:37:57 -05:00
|
|
|
const FACE_ROTATION: &[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
|
|
|
];
|
|
|
|
|
2018-11-04 16:37:57 -05:00
|
|
|
const FACE_ROTATION_X: &[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
|
|
|
];
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
fn rotate_direction(
|
|
|
|
val: Direction,
|
|
|
|
offset: i32,
|
|
|
|
rots: &[Direction],
|
|
|
|
invalid: &[Direction],
|
|
|
|
) -> Direction {
|
2016-03-24 11:39:57 -04:00
|
|
|
for d in invalid {
|
|
|
|
if *d == val {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
let pos = rots.iter().position(|v| *v == val).unwrap_or(0) as i32;
|
2016-03-29 08:44:46 -04:00
|
|
|
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
|
2019-05-08 22:31:09 -04:00
|
|
|
self.models.choose(rng).unwrap()
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum BuiltinType {
|
|
|
|
False,
|
|
|
|
Generated,
|
|
|
|
Entity,
|
|
|
|
Compass,
|
2020-06-21 15:17:24 -04:00
|
|
|
Clock,
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
|
2021-12-24 12:17:58 -05:00
|
|
|
#[allow(dead_code)]
|
2016-03-24 11:39:57 -04:00
|
|
|
display: HashMap<String, ModelDisplay, BuildHasherDefault<FNVHash>>,
|
2021-12-24 12:17:58 -05:00
|
|
|
#[allow(dead_code)]
|
2016-03-24 11:39:57 -04:00
|
|
|
builtin: BuiltinType,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RawModel {
|
|
|
|
fn lookup_texture(&self, name: &str) -> String {
|
|
|
|
if !name.is_empty() && name.starts_with('#') {
|
2020-06-21 15:17:24 -04:00
|
|
|
let tex = self
|
|
|
|
.texture_vars
|
|
|
|
.get(&name[1..])
|
|
|
|
.cloned()
|
2020-06-30 22:09:15 -04:00
|
|
|
.unwrap_or_else(|| "".to_owned());
|
2016-03-24 11:39:57 -04:00
|
|
|
return self.lookup_texture(&tex);
|
|
|
|
}
|
|
|
|
name.to_owned()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2021-12-24 12:17:58 -05:00
|
|
|
#[allow(dead_code)]
|
2016-03-24 11:39:57 -04:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
1.13.2 (404) multiprotocol support (#67)
Adds support for 1.13.2 protocol (404)
Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support
Metadata:
* Support 1.13.2 slot data format, bool and varint item id, optional damage (moved to NBT)
https://wiki.vg/index.php?title=Slot_Data&type=revision&diff=14363&oldid=7835
Packets:
* Add 1.13.2 packets, and implement all the command data parsers
https://wiki.vg/Command_Data#Parsers
* Send new plugin channel minecraft:brand
https://wiki.vg/Plugin_channels#minecraft:brand
* Add 1.13.2 metadata format, with shifted IDs
https://wiki.vg/Entity_metadata#Entity_Metadata_Format
* Implement particle entity metadata
* Add structures for 16 new packets
Blocks: The Flattening:
* Assign flattened IDs in correct order using new 'offset' macro token
* Assign hierarchical (pre-flattening) block IDs sequentially by counting Some data
* Split VANILLA_ID_MAP into flat/hier struct, to support before and after the flattening
* Extend travis build time to 20 minutes because the blocks macro takes a long time
* Support both flat/hier blocks by passing protocol_version to by_vanilla_id
Add block states and offsets for all blocks, replacing metadata for 1.13+:
* Add stripped logs and what was Log2 to Log
* Add the Wood blocks, should be called bark, previously Axis::None Log
* Add leaves distance and offset
* Add jungle/acacia to Leaves moved from Leaves2
* Add dispenser offsets, direction
* Add note block states
* Add offset None to Missing253 and Missing254, no holes in block states of 1.13.2
* Add bed colors
* Add seagrass, tall seagrass, remove redundant deadgrass, and piston offset
* Add torch, TNT, fire offsets, remove slabs
* Add furnance offset, merges lit into a property
* Add pressure plate offsets, new pressure plates, redstone ore/lit merged
* Add lever offsets, new directions from ceiling/floor, rename LeverDirections
* Add redstone torch offsets, new blocks since lit/unlit is now merged, and standing/wall is split
* Change lever to split face/facing, rm LeverDirection, add AttachedFace
* Add stone button offsets, face/facing similar to lever
* Move face/facing data and variant to AttachedFace, reuse for lever/stonebutton
* Add data_with_facing_and_powered() to AttachedFace, for lever/stonebutton
* Add wooden button offsets each wood
* Add pumpkin without a face
* Add carved pumpkin, portal offsets
* Add lit pumpkin (as jack-o-lantern) offsets after carved pumpkin
* Add repeater offsets, merged into Repeater
* Change brown mushroom block to booleans instead of MushroomVariant
* Add mushroom block offsets, red/brown mushroom blocks, and a new mushroom stem block
* Add command block, cobblestone walls, and flower pot offsets
Empty flower pot, and potted plants including saplings. Rename
variant DarkOak to DarkOakSaplings because it is a sapling, and
remove the duplicate Dandelion variant which causes duplicate blocks.
* Increase recursion limit in steven_blocks
* Add colored banner offsets
* Add wooden slab including double slab, in a different position for pre-1.13 and 1.13
* StoneSlabVariant::Wood -> StoneSlabVariant::PetrifiedWood
* Add fence_gate_offset() for wooden fence gates
* Add frosted ice age, offset
* Add new blocks: kelp, turtle egg, coral, coral fans, sea pickle, blue ice, smooth stone
* Add new blocks: conduit, void air, cave aid, bubble column, last of the 1.13 blocks
2018-12-29 00:11:42 -05:00
|
|
|
#[derive(Clone, Debug)]
|
2016-03-24 11:39:57 -04:00
|
|
|
struct Model {
|
|
|
|
faces: Vec<Face>,
|
|
|
|
ambient_occlusion: bool,
|
2021-12-24 12:17:58 -05:00
|
|
|
#[allow(dead_code)]
|
2016-03-24 11:39:57 -04:00
|
|
|
weight: f64,
|
|
|
|
}
|
|
|
|
|
1.13.2 (404) multiprotocol support (#67)
Adds support for 1.13.2 protocol (404)
Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support
Metadata:
* Support 1.13.2 slot data format, bool and varint item id, optional damage (moved to NBT)
https://wiki.vg/index.php?title=Slot_Data&type=revision&diff=14363&oldid=7835
Packets:
* Add 1.13.2 packets, and implement all the command data parsers
https://wiki.vg/Command_Data#Parsers
* Send new plugin channel minecraft:brand
https://wiki.vg/Plugin_channels#minecraft:brand
* Add 1.13.2 metadata format, with shifted IDs
https://wiki.vg/Entity_metadata#Entity_Metadata_Format
* Implement particle entity metadata
* Add structures for 16 new packets
Blocks: The Flattening:
* Assign flattened IDs in correct order using new 'offset' macro token
* Assign hierarchical (pre-flattening) block IDs sequentially by counting Some data
* Split VANILLA_ID_MAP into flat/hier struct, to support before and after the flattening
* Extend travis build time to 20 minutes because the blocks macro takes a long time
* Support both flat/hier blocks by passing protocol_version to by_vanilla_id
Add block states and offsets for all blocks, replacing metadata for 1.13+:
* Add stripped logs and what was Log2 to Log
* Add the Wood blocks, should be called bark, previously Axis::None Log
* Add leaves distance and offset
* Add jungle/acacia to Leaves moved from Leaves2
* Add dispenser offsets, direction
* Add note block states
* Add offset None to Missing253 and Missing254, no holes in block states of 1.13.2
* Add bed colors
* Add seagrass, tall seagrass, remove redundant deadgrass, and piston offset
* Add torch, TNT, fire offsets, remove slabs
* Add furnance offset, merges lit into a property
* Add pressure plate offsets, new pressure plates, redstone ore/lit merged
* Add lever offsets, new directions from ceiling/floor, rename LeverDirections
* Add redstone torch offsets, new blocks since lit/unlit is now merged, and standing/wall is split
* Change lever to split face/facing, rm LeverDirection, add AttachedFace
* Add stone button offsets, face/facing similar to lever
* Move face/facing data and variant to AttachedFace, reuse for lever/stonebutton
* Add data_with_facing_and_powered() to AttachedFace, for lever/stonebutton
* Add wooden button offsets each wood
* Add pumpkin without a face
* Add carved pumpkin, portal offsets
* Add lit pumpkin (as jack-o-lantern) offsets after carved pumpkin
* Add repeater offsets, merged into Repeater
* Change brown mushroom block to booleans instead of MushroomVariant
* Add mushroom block offsets, red/brown mushroom blocks, and a new mushroom stem block
* Add command block, cobblestone walls, and flower pot offsets
Empty flower pot, and potted plants including saplings. Rename
variant DarkOak to DarkOakSaplings because it is a sapling, and
remove the duplicate Dandelion variant which causes duplicate blocks.
* Increase recursion limit in steven_blocks
* Add colored banner offsets
* Add wooden slab including double slab, in a different position for pre-1.13 and 1.13
* StoneSlabVariant::Wood -> StoneSlabVariant::PetrifiedWood
* Add fence_gate_offset() for wooden fence gates
* Add frosted ice age, offset
* Add new blocks: kelp, turtle egg, coral, coral fans, sea pickle, blue ice, smooth stone
* Add new blocks: conduit, void air, cave aid, bubble column, last of the 1.13 blocks
2018-12-29 00:11:42 -05:00
|
|
|
#[derive(Clone, Debug)]
|
2016-03-24 11:39:57 -04:00
|
|
|
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,
|
2021-12-24 12:17:58 -05:00
|
|
|
#[allow(dead_code)]
|
2016-03-24 11:39:57 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -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),
|
2020-06-21 15:17:24 -04:00
|
|
|
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,
|
|
|
|
),
|
2016-04-04 10:05:24 -04:00
|
|
|
}
|
|
|
|
} 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,
|
2020-06-21 15:17:24 -04:00
|
|
|
x,
|
|
|
|
y,
|
|
|
|
z,
|
2016-03-24 11:39:57 -04:00
|
|
|
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,
|
2020-06-21 15:17:24 -04:00
|
|
|
this_mat.force_shade,
|
2016-03-24 11:39:57 -04:00
|
|
|
);
|
|
|
|
vert.block_light = bl;
|
|
|
|
vert.sky_light = sl;
|
|
|
|
vert.write(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
indices
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
fn calculate_biome(
|
|
|
|
snapshot: &world::Snapshot,
|
|
|
|
x: i32,
|
|
|
|
z: i32,
|
|
|
|
img: &image::DynamicImage,
|
|
|
|
) -> (u8, u8, u8) {
|
|
|
|
use std::cmp::{max, min};
|
2016-03-24 15:39:14 -04:00
|
|
|
let mut count = 0;
|
|
|
|
let mut r = 0;
|
|
|
|
let mut g = 0;
|
|
|
|
let mut b = 0;
|
2020-06-21 15:17:24 -04:00
|
|
|
for xx in -1..2 {
|
|
|
|
for zz in -1..2 {
|
|
|
|
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);
|
2020-01-05 12:10:59 -05:00
|
|
|
r += col.0[0] as u32;
|
|
|
|
g += col.0[1] as u32;
|
|
|
|
b += col.0[2] as u32;
|
2016-03-24 15:39:14 -04:00
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
2020-06-21 15:17:24 -04:00
|
|
|
((r / count) as u8, (g / count) as u8, (b / count) as u8)
|
2016-03-24 15:39:14 -04:00
|
|
|
}
|
2016-03-24 11:39:57 -04:00
|
|
|
|
2020-06-21 15:17:24 -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) {
|
2018-11-04 14:48:03 -05:00
|
|
|
use crate::world::block;
|
2020-06-21 15:17:24 -04:00
|
|
|
use std::cmp::max;
|
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;
|
|
|
|
|
2020-01-04 22:09:00 -05:00
|
|
|
for ox in [-0.6, 0.0].iter() {
|
|
|
|
for oy in [-0.6, 0.0].iter() {
|
|
|
|
for oz in [-0.6, 0.0].iter() {
|
2016-03-24 11:39:57 -04:00
|
|
|
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);
|
2020-10-10 17:31:02 -04:00
|
|
|
if (force && !matches!(snapshot.get_block(lx, ly, lz), block::Air {}))
|
2020-06-21 15:17:24 -04:00
|
|
|
|| (sl == 0 && bl == 0)
|
|
|
|
{
|
2016-03-24 11:39:57 -04:00
|
|
|
bl = s_block_light;
|
|
|
|
sl = s_sky_light;
|
|
|
|
}
|
|
|
|
block_light += bl as u32;
|
|
|
|
sky_light += sl as u32;
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 15:17:24 -04:00
|
|
|
(
|
|
|
|
(((block_light * 4000) / count) as u16),
|
|
|
|
(((sky_light * 4000) / count) as u16),
|
|
|
|
)
|
2016-03-24 11:39:57 -04:00
|
|
|
}
|
2016-03-26 07:46:37 -04:00
|
|
|
|
2018-11-04 16:37:57 -05:00
|
|
|
pub const PRECOMPUTED_VERTS: [&[BlockVertex; 4]; 6] = [
|
2020-06-21 15:17:24 -04:00
|
|
|
&[
|
|
|
|
// Up
|
2016-03-26 07:46:37 -04:00
|
|
|
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),
|
|
|
|
],
|
2020-06-21 15:17:24 -04:00
|
|
|
&[
|
|
|
|
// Down
|
2016-03-26 07:46:37 -04:00
|
|
|
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),
|
|
|
|
],
|
2020-06-21 15:17:24 -04:00
|
|
|
&[
|
|
|
|
// North
|
2016-03-26 07:46:37 -04:00
|
|
|
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),
|
|
|
|
],
|
2020-06-21 15:17:24 -04:00
|
|
|
&[
|
|
|
|
// South
|
2016-03-26 07:46:37 -04:00
|
|
|
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),
|
|
|
|
],
|
2020-06-21 15:17:24 -04:00
|
|
|
&[
|
|
|
|
// West
|
2016-03-26 07:46:37 -04:00
|
|
|
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),
|
|
|
|
],
|
2020-06-21 15:17:24 -04:00
|
|
|
&[
|
|
|
|
// East
|
2016-03-26 07:46:37 -04:00
|
|
|
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),
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
1.13.2 (404) multiprotocol support (#67)
Adds support for 1.13.2 protocol (404)
Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support
Metadata:
* Support 1.13.2 slot data format, bool and varint item id, optional damage (moved to NBT)
https://wiki.vg/index.php?title=Slot_Data&type=revision&diff=14363&oldid=7835
Packets:
* Add 1.13.2 packets, and implement all the command data parsers
https://wiki.vg/Command_Data#Parsers
* Send new plugin channel minecraft:brand
https://wiki.vg/Plugin_channels#minecraft:brand
* Add 1.13.2 metadata format, with shifted IDs
https://wiki.vg/Entity_metadata#Entity_Metadata_Format
* Implement particle entity metadata
* Add structures for 16 new packets
Blocks: The Flattening:
* Assign flattened IDs in correct order using new 'offset' macro token
* Assign hierarchical (pre-flattening) block IDs sequentially by counting Some data
* Split VANILLA_ID_MAP into flat/hier struct, to support before and after the flattening
* Extend travis build time to 20 minutes because the blocks macro takes a long time
* Support both flat/hier blocks by passing protocol_version to by_vanilla_id
Add block states and offsets for all blocks, replacing metadata for 1.13+:
* Add stripped logs and what was Log2 to Log
* Add the Wood blocks, should be called bark, previously Axis::None Log
* Add leaves distance and offset
* Add jungle/acacia to Leaves moved from Leaves2
* Add dispenser offsets, direction
* Add note block states
* Add offset None to Missing253 and Missing254, no holes in block states of 1.13.2
* Add bed colors
* Add seagrass, tall seagrass, remove redundant deadgrass, and piston offset
* Add torch, TNT, fire offsets, remove slabs
* Add furnance offset, merges lit into a property
* Add pressure plate offsets, new pressure plates, redstone ore/lit merged
* Add lever offsets, new directions from ceiling/floor, rename LeverDirections
* Add redstone torch offsets, new blocks since lit/unlit is now merged, and standing/wall is split
* Change lever to split face/facing, rm LeverDirection, add AttachedFace
* Add stone button offsets, face/facing similar to lever
* Move face/facing data and variant to AttachedFace, reuse for lever/stonebutton
* Add data_with_facing_and_powered() to AttachedFace, for lever/stonebutton
* Add wooden button offsets each wood
* Add pumpkin without a face
* Add carved pumpkin, portal offsets
* Add lit pumpkin (as jack-o-lantern) offsets after carved pumpkin
* Add repeater offsets, merged into Repeater
* Change brown mushroom block to booleans instead of MushroomVariant
* Add mushroom block offsets, red/brown mushroom blocks, and a new mushroom stem block
* Add command block, cobblestone walls, and flower pot offsets
Empty flower pot, and potted plants including saplings. Rename
variant DarkOak to DarkOakSaplings because it is a sapling, and
remove the duplicate Dandelion variant which causes duplicate blocks.
* Increase recursion limit in steven_blocks
* Add colored banner offsets
* Add wooden slab including double slab, in a different position for pre-1.13 and 1.13
* StoneSlabVariant::Wood -> StoneSlabVariant::PetrifiedWood
* Add fence_gate_offset() for wooden fence gates
* Add frosted ice age, offset
* Add new blocks: kelp, turtle egg, coral, coral fans, sea pickle, blue ice, smooth stone
* Add new blocks: conduit, void air, cave aid, bubble column, last of the 1.13 blocks
2018-12-29 00:11:42 -05:00
|
|
|
#[derive(Clone, Debug)]
|
2016-03-26 07:46:37 -04:00
|
|
|
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 {
|
2020-06-21 15:17:24 -04:00
|
|
|
x,
|
|
|
|
y,
|
|
|
|
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,
|
2016-03-26 07:46:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|