diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..6ef8d1a11ed0a8bcbd16f7d98789ae32f9fe504a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'erroccfisumreg'", + "cargo": { + "args": [ + "build", + "--bin=erroccfisumreg", + "--package=erroccfisumreg" + ], + "filter": { + "name": "erroccfisumreg", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'erroccfisumreg'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=erroccfisumreg", + "--package=erroccfisumreg" + ], + "filter": { + "name": "erroccfisumreg", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cfb15f92b9e88aa11ba0baca2bfcab3cf7b0b4ea..abb1f9b6a283c527d6099518193bedebc0a718e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,6 +721,9 @@ dependencies = [ "egui_winit_vulkano", "obj", "rodio", + "serde", + "serde_json", + "utf-8", "vulkano", "vulkano-shaders", "vulkano-util", @@ -2212,6 +2215,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 20f20fe1cabcd97bd10e47b5b1dfb30c80688fab..06d14320bbe8ad9b3d6ed26242d53ff3d2a9ae85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,10 @@ egui_winit_vulkano = "0.22" ash = "*" +serde = { version = "*", features = ["derive"] } +serde_json = "*" + +utf-8 = "*" # using latest gits [patch.crates-io] diff --git a/src/gui.rs b/src/gui.rs index c6556e3e4b07facbdd1d5ff28c2bf395f915e0dc..7649105489edeca0e1052da65699564e46a6ac86 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -4,7 +4,7 @@ use egui::{ }; use egui_winit_vulkano::Gui; -use crate::objects::{Light, Mesh}; +use crate::objects::{Light, Mesh, CSG}; fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) { ui.label(egui::RichText::new(text).size(size)); @@ -17,6 +17,7 @@ pub struct GState { pub meshes: Vec<Mesh>, pub lights: Vec<Light>, + pub csg: Vec<CSG>, pub fps: [f64; 128], } @@ -29,6 +30,7 @@ impl Default for GState { meshes: vec![], lights: vec![], + csg: vec![], fps: [0.0; 128], } diff --git a/src/main.rs b/src/main.rs index cb480fa4da95cb79f58ffac0da86f667fca77add..9ebea850ad6b694eaa2d431f0105b3e571df2cd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(variant_count)] use cgmath::{Deg, EuclideanSpace, Euler, Matrix3, Matrix4, Point3, Rad, SquareMatrix, Vector3}; use std::io::Cursor; use std::{sync::Arc, time::Instant}; @@ -18,7 +19,7 @@ use vulkano::pipeline::graphics::vertex_input::Vertex; use vulkano::pipeline::PipelineBindPoint; use vulkano::shader::{ShaderModule, SpecializationConstants}; use vulkano::swapchain::{PresentMode, SwapchainPresentInfo}; -use vulkano::{Version, VulkanLibrary}; +use vulkano::VulkanLibrary; use winit::event::{DeviceEvent, ElementState, MouseButton, VirtualKeyCode}; use egui_winit_vulkano::Gui; @@ -58,6 +59,7 @@ mod gui; use crate::gui::*; mod objects; use crate::objects::*; +mod mcsg_deserialise; pub type MemoryAllocator = StandardMemoryAllocator; @@ -370,11 +372,23 @@ fn main() { d: false, }; - gstate.meshes.push(load_obj( - &memory_allocator, - &mut Cursor::new(PLATONIC_SOLIDS[0].1), - PLATONIC_SOLIDS[0].0.to_string(), - )); + gstate.meshes.push( + load_obj( + &memory_allocator, + &mut Cursor::new(PLATONIC_SOLIDS[0].1), + PLATONIC_SOLIDS[0].0.to_string(), + ) + .unwrap(), + ); + + gstate.csg.push( + load_csg( + &memory_allocator, + &mut Cursor::new(CSG_SOLIDS[0].1), + CSG_SOLIDS[0].0.to_string(), + ) + .unwrap(), + ); gstate .lights diff --git a/src/mcsg_deserialise.rs b/src/mcsg_deserialise.rs new file mode 100644 index 0000000000000000000000000000000000000000..b81f5bdb60e0ae8049fab811c7ced612923bb643 --- /dev/null +++ b/src/mcsg_deserialise.rs @@ -0,0 +1,516 @@ +use std::fmt::Display; +use std::io::{BufReader, Cursor, Read}; +use std::ops::{AddAssign, MulAssign, Neg}; +use std::str::Chars; + +use serde::de::{ + self, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess, + Visitor, +}; +use serde::{forward_to_deserialize_any, Deserialize}; +use utf8::{BufReadDecoder, BufReadDecoderError}; + +type Result<T> = core::result::Result<T, Error>; + +#[derive(Debug)] +pub enum Error { + Message(String), + UTF8(String), + TrailingCharacters, + Eof, + ExpectedString, + Syntax, + ExpectedArrayEnd, + ExpectedArray, + ExpectedMapEnd, + ExpectedMap, + ExpectedMapColon, + ExpectedEnum, +} +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::Message(msg) | Error::UTF8(msg) => f.write_str(msg), + _ => f.write_fmt(format_args!("Error: {:?}", self)), + } + } +} +impl std::error::Error for Error {} +impl serde::de::Error for Error { + fn custom<T: Display>(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} +impl serde::ser::Error for Error { + fn custom<T: Display>(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +pub struct Deserializer<'de> { + // This string starts with the input data and characters are truncated off + // the beginning as data is parsed. + input: BufReadDecoder<BufReader<&'de mut dyn Read>>, + read_buf: String, + peek: Option<(char, usize)>, +} + +impl<'de> Deserializer<'de> { + // By convention, `Deserializer` constructors are named like `from_xyz`. + // That way basic use cases are satisfied by something like + // `serde_json::from_str(...)` while advanced use cases that require a + // deserializer can make one with `serde_json::Deserializer::from_str(...)`. + + pub fn from_reader(input: &'de mut dyn Read) -> Self { + Deserializer { + input: BufReadDecoder::new(BufReader::new(input)), + read_buf: String::new(), + peek: None, + } + } +} + +// By convention, the public API of a Serde deserializer is one or more +// `from_xyz` methods such as `from_str`, `from_bytes`, or `from_reader` +// depending on what Rust types the deserializer is able to consume as input. +// +// This basic deserializer supports only `from_str`. +pub fn from_reader<'a, T>(s: &'a mut dyn Read) -> Result<T> +where + T: Deserialize<'a>, +{ + let mut deserializer = Deserializer::from_reader(s); + let t = T::deserialize(&mut deserializer)?; + if deserializer.check_end() { + Ok(t) + } else { + Err(Error::TrailingCharacters) + } +} + +fn get_char(mut s: Chars) -> Result<(char, usize)> { + let mut count = 0usize; + loop { + let next = s.next().ok_or(Error::Eof); + count += 1; + if next.as_ref().map_or(false, |&c| c.is_whitespace()) { + continue; + } + if next.as_ref().map_or(false, |this| this == &'/') { + if s.next().map_or(false, |this| this == '/') { + while s.next().map_or(false, |s| s != '\n' && s != '\r') { + count += 1; + } + count += 1; + } + count += 1; + } else { + return next.map(|c| (c, count)); + } + } +} + +// SERDE IS NOT A PARSING LIBRARY. This impl block defines a few basic parsing +// functions from scratch. More complicated formats may wish to use a dedicated +// parsing library to help implement their Serde deserializer. +impl<'de> Deserializer<'de> { + fn check_end(&mut self) -> bool { + self.input.next_strict().is_none() + } + + // Look at the first character in the input without consuming it. + fn peek_char(&mut self) -> Result<(char, usize)> { + if let Some(p) = self.peek { + Ok(p) + } else { + loop { + match get_char(self.read_buf.chars()) { + Ok(c) => { + self.peek = Some(c); + return Ok(c); + } + Err(Error::Eof) => match self.input.next_strict() { + Some(e) => { + match e { + Ok(s) => { + s.clone_into(&mut self.read_buf); + } + Err(e) => return Err(Error::UTF8(format!("{}", e))), + }; + } + None => return Err(Error::Eof), + }, + Err(e) => return Err(e), + } + } + } + } + + // Consume the first character in the input. + fn next_char(&mut self) -> Result<char> { + let ch = self.peek_char()?; + self.read_buf = self.read_buf[ch.1..].to_string(); + self.peek = None; + Ok(ch.0) + } + + // Parse a string until the next '"' character. + // + // Makes no attempt to handle escape sequences. What did you expect? This is + // example code! + fn parse_string(&mut self) -> Result<String> { + if self.next_char()? != '"' { + return Err(Error::ExpectedString); + } + loop { + match self.read_buf.find('"') { + Some(len) => { + let out = self.read_buf[..len].to_string(); + self.read_buf = self.read_buf[len + 1..].to_string(); + return Ok(out); + } + None => match self.input.next_strict() { + Some(e) => { + match e { + Ok(s) => { + s.clone_into(&mut self.read_buf); + } + Err(e) => return Err(Error::UTF8(format!("{}", e))), + }; + } + None => return Err(Error::Eof), + }, + } + } + } +} + +impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { + type Error = Error; + + // Look at the input data to decide what Serde data model type to + // deserialize as. Not all data formats are able to support this operation. + // Formats that support `deserialize_any` are known as self-describing. + fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + match self.peek_char()?.0 { + '"' => self.deserialize_str(visitor), + '[' => self.deserialize_seq(visitor), + '{' => self.deserialize_map(visitor), + _ => Err(Error::Syntax), + } + } + + // Refer to the "Understanding deserializer lifetimes" page for information + // about the three deserialization flavors of strings in Serde. + fn deserialize_string<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.visit_string(self.parse_string()?) + } + + fn deserialize_str<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_string(visitor) + } + + // Deserialization of compound types like sequences and maps happens by + // passing the visitor an "Access" object that gives it the ability to + // iterate through the data contained in the sequence. + fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + // Parse the opening bracket of the sequence. + if self.next_char()? == '[' { + // Give the visitor access to each element of the sequence. + let value = visitor.visit_seq(CommaSeparated::new(self))?; + // Parse the closing bracket of the sequence. + if self.next_char()? == ']' { + Ok(value) + } else { + Err(Error::ExpectedArrayEnd) + } + } else { + Err(Error::ExpectedArray) + } + } + + // Much like `deserialize_seq` but calls the visitors `visit_map` method + // with a `MapAccess` implementation, rather than the visitor's `visit_seq` + // method with a `SeqAccess` implementation. + fn deserialize_map<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + // Parse the opening brace of the map. + if self.next_char()? == '{' { + // Give the visitor access to each entry of the map. + let value = visitor.visit_map(CommaSeparated::new(self))?; + // Parse the closing brace of the map. + if self.next_char()? == '}' { + Ok(value) + } else { + Err(Error::ExpectedMapEnd) + } + } else { + Err(Error::ExpectedMap) + } + } + + // Structs look just like maps in JSON. + // + // Notice the `fields` parameter - a "struct" in the Serde data model means + // that the `Deserialize` implementation is required to know what the fields + // are before even looking at the input data. Any key-value pairing in which + // the fields cannot be known ahead of time is probably a map. + fn deserialize_struct<V>( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_map(visitor) + } + + // An identifier in Serde is the type that identifies a field of a struct or + // the variant of an enum. In JSON, struct fields and enum variants are + // represented as strings. In other formats they may be represented as + // numeric indices. + fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + // Tuples look just like sequences in JSON. Some formats may be able to + // represent tuples more efficiently. + // + // As indicated by the length parameter, the `Deserialize` implementation + // for a tuple in the Serde data model is required to know the length of the + // tuple before even looking at the input data. + fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + // Tuple structs look just like sequences in JSON. + fn deserialize_tuple_struct<V>( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_enum<V>( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result<V::Value> + where + V: Visitor<'de>, + { + if self.peek_char()?.0 == '"' { + // Visit a unit variant. + visitor.visit_enum(self.parse_string()?.into_deserializer()) + } else if self.next_char()? == '{' { + // Visit a newtype variant, tuple variant, or struct variant. + let value = visitor.visit_enum(Enum::new(self))?; + // Parse the matching close brace. + if self.next_char()? == '}' { + Ok(value) + } else { + Err(Error::ExpectedMapEnd) + } + } else { + Err(Error::ExpectedEnum) + } + } + + // As is done here, serializers are encouraged to treat newtype structs as + // insignificant wrappers around the data they contain. That means not + // parsing anything other than the contained value. + fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + // Like `deserialize_any` but indicates to the `Deserializer` that it makes + // no difference which `Visitor` method is called because the data is + // ignored. + // + // Some deserializers are able to implement this more efficiently than + // `deserialize_any`, for example by rapidly skipping over matched + // delimiters without paying close attention to the data in between. + // + // Some formats are not able to implement this at all. Formats that can + // implement `deserialize_any` and `deserialize_ignored_any` are known as + // self-describing. + fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + self.deserialize_any(visitor) + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char bytes byte_buf option unit unit_struct + } +} + +// In order to handle commas correctly when deserializing a JSON array or map, +// we need to track whether we are on the first element or past the first +// element. +struct CommaSeparated<'a, 'de: 'a> { + de: &'a mut Deserializer<'de>, +} + +impl<'a, 'de> CommaSeparated<'a, 'de> { + fn new(de: &'a mut Deserializer<'de>) -> Self { + CommaSeparated { de } + } +} + +// `SeqAccess` is provided to the `Visitor` to give it the ability to iterate +// through elements of the sequence. +impl<'de, 'a> SeqAccess<'de> for CommaSeparated<'a, 'de> { + type Error = Error; + + fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>> + where + T: DeserializeSeed<'de>, + { + // Check if there are no more elements. + if self.de.peek_char()?.0 == ']' { + return Ok(None); + } + // Deserialize an array element. + seed.deserialize(&mut *self.de).map(Some) + } +} + +// `MapAccess` is provided to the `Visitor` to give it the ability to iterate +// through entries of the map. +impl<'de, 'a> MapAccess<'de> for CommaSeparated<'a, 'de> { + type Error = Error; + + fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>> + where + K: DeserializeSeed<'de>, + { + // Check if there are no more entries. + if self.de.peek_char()?.0 == '}' { + return Ok(None); + } + // Deserialize a map key. + seed.deserialize(&mut *self.de).map(Some) + } + + fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value> + where + V: DeserializeSeed<'de>, + { + // It doesn't make a difference whether the colon is parsed at the end + // of `next_key_seed` or at the beginning of `next_value_seed`. In this + // case the code is a bit simpler having it here. + if self.de.next_char()? != ':' { + return Err(Error::ExpectedMapColon); + } + // Deserialize a map value. + seed.deserialize(&mut *self.de) + } +} + +struct Enum<'a, 'de: 'a> { + de: &'a mut Deserializer<'de>, +} + +impl<'a, 'de> Enum<'a, 'de> { + fn new(de: &'a mut Deserializer<'de>) -> Self { + Enum { de } + } +} + +// `EnumAccess` is provided to the `Visitor` to give it the ability to determine +// which variant of the enum is supposed to be deserialized. +// +// Note that all enum deserialization methods in Serde refer exclusively to the +// "externally tagged" enum representation. +impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> { + type Error = Error; + type Variant = Self; + + fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)> + where + V: DeserializeSeed<'de>, + { + // The `deserialize_enum` method parsed a `{` character so we are + // currently inside of a map. The seed will be deserializing itself from + // the key of the map. + let val = seed.deserialize(&mut *self.de)?; + // Parse the colon separating map key from value. + if self.de.next_char()? == ':' { + Ok((val, self)) + } else { + Err(Error::ExpectedMapColon) + } + } +} + +// `VariantAccess` is provided to the `Visitor` to give it the ability to see +// the content of the single variant that it decided to deserialize. +impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { + type Error = Error; + + // If the `Visitor` expected this variant to be a unit variant, the input + // should have been the plain string case handled in `deserialize_enum`. + fn unit_variant(self) -> Result<()> { + Err(Error::ExpectedString) + } + + // Newtype variants are represented in JSON as `{ NAME: VALUE }` so + // deserialize the value here. + fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value> + where + T: DeserializeSeed<'de>, + { + seed.deserialize(self.de) + } + + // Tuple variants are represented in JSON as `{ NAME: [DATA...] }` so + // deserialize the sequence of data here. + fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + de::Deserializer::deserialize_seq(self.de, visitor) + } + + // Struct variants are represented in JSON as `{ NAME: { K: V, ... } }` so + // deserialize the inner map here. + fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value> + where + V: Visitor<'de>, + { + de::Deserializer::deserialize_map(self.de, visitor) + } +} diff --git a/src/objects.rs b/src/objects.rs index d974887d6522ed390a75bbfece658a531925d157..55f866704ed3570ebfdca00dba32edc109591270 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -1,16 +1,27 @@ -use std::{collections::HashMap, io::Read}; +use std::{ + collections::HashMap, + io::{Cursor, Read}, + mem, + num::ParseFloatError, +}; use bytemuck::{Pod, Zeroable}; -use cgmath::{Deg, Euler, Point3, Vector3}; -use obj::{LoadConfig, ObjData}; +use cgmath::{Deg, EuclideanSpace, Euler, Matrix3, Point3, SquareMatrix, Vector3}; +use obj::{LoadConfig, ObjData, ObjError}; +use serde::{Deserialize, Serialize}; use vulkano::{ buffer::{Buffer, BufferAllocateInfo, BufferUsage, Subbuffer}, + half::f16, pipeline::graphics::vertex_input::Vertex, }; -use crate::MemoryAllocator; +use crate::{ + mcsg_deserialise::{from_reader, Deserializer}, + MemoryAllocator, +}; pub const PLATONIC_SOLIDS: [(&str, &[u8]); 1] = [("Buny", include_bytes!("bunny.obj"))]; +pub const CSG_SOLIDS: [(&str, &[u8]); 1] = [("Primitives", include_bytes!("primitive.mcsg"))]; // We now create a buffer that will store the shape of our triangle. // We use #[repr(C)] here to force rustc to not do anything funky with our data, although for this @@ -31,11 +42,54 @@ pub struct Mesh { pub indices: Subbuffer<[u32]>, pub pos: Point3<f32>, pub rot: Euler<Deg<f32>>, - pub scale: f32, + pub scale: Vector3<f32>, +} + +#[derive(Debug)] +pub struct CSG { + pub name: String, + pub parts: Subbuffer<[CSGPart]>, + pub pos: Point3<f32>, + pub rot: Euler<Deg<f32>>, + pub scale: Vector3<f32>, } -pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name: String) -> Mesh { - let object = ObjData::load_buf_with_config(input, LoadConfig::default()).unwrap(); +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod, Vertex)] +pub struct CSGPart { + #[format(R16_SFLOAT)] + code: u16, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Default)] +pub enum CSGOpcode { + Infinity = 0, // 0 is actually FP16 infinity, perfectly normal representation + #[default] + None, + Const, +} + +impl CSGPart { + pub fn opcode(opcode: CSGOpcode) -> CSGPart { + CSGPart { + code: opcode as u16 | 0b0111110000000000, + } + } + + pub fn literal(literal: f16) -> CSGPart { + CSGPart { + code: literal.to_bits(), + } + } +} + +pub fn load_obj( + memory_allocator: &MemoryAllocator, + input: &mut dyn Read, + name: String, +) -> Result<Mesh, ObjError> { + let object = ObjData::load_buf_with_config(input, LoadConfig::default())?; let mut vertices = vec![]; @@ -87,7 +141,7 @@ pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name: ) .unwrap(); - Mesh { + Ok(Mesh { vertices: vertex_buffer, indices: index_buffer, pos: Point3 { @@ -96,9 +150,237 @@ pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name: z: 0., }, rot: Euler::new(Deg(0.), Deg(0.), Deg(0.)), - scale: 1., + scale: Vector3 { + x: 1., + y: 1., + z: 1., + }, name, - } + }) +} + +#[derive(Serialize, Deserialize)] +struct MCSG { + object: Vec<MCSGObject>, + csg: Vec<MCSGCSG>, +} +type MCSGObject = HashMap<String, String>; +type MCSGCSG = Vec<MCSGCSGPart>; +type MCSGCSGPart = HashMap<String, String>; + +fn matrix3_from_string(input: &String) -> Result<Matrix3<f32>, String> { + let vec = input + .split(" ") + .map(|s| s.parse::<f32>()) + .collect::<Result<Vec<f32>, _>>() + .map_err(|_| "not floats")?; + let array: [f32; 9] = vec.try_into().map_err(|_| "wrong number of values")?; + let matrix: &Matrix3<f32> = (&array).into(); + Ok(*matrix) +} + +fn vector3_from_string(input: &String) -> Result<Vector3<f32>, String> { + let vec = input + .split(" ") + .map(|s| s.parse::<f32>()) + .collect::<Result<Vec<f32>, _>>() + .map_err(|_| "not floats")?; + let array: [f32; 3] = vec.try_into().map_err(|_| "wrong number of values")?; + let vector: &Vector3<f32> = (&array).into(); + Ok(*vector) +} + +fn point3_from_string(input: &String) -> Result<Point3<f32>, String> { + let vec = input + .split(" ") + .map(|s| s.parse::<f32>()) + .collect::<Result<Vec<f32>, _>>() + .map_err(|_| "not floats")?; + let array: [f32; 3] = vec.try_into().map_err(|_| "wrong number of values")?; + let point: &Point3<f32> = (&array).into(); + Ok(*point) +} +struct TRS { + translation: Point3<f32>, + rotation: Matrix3<f32>, + scale: Vector3<f32>, +} + +fn get_trs(o: &HashMap<String, String>) -> Result<TRS, String> { + Ok(TRS { + translation: o + .get("t") + .map(point3_from_string) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(Point3::origin()), + rotation: o + .get("r") + .map(matrix3_from_string) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(Matrix3::identity()), + scale: o + .get("s") + .map(vector3_from_string) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(Vector3 { + x: 1., + y: 1., + z: 1., + }), + }) +} +fn get_color(o: &HashMap<String, String>) -> Result<Vector3<f32>, String> { + Ok(o.get("color") + .map(vector3_from_string) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(Vector3 { + x: 255., + y: 255., + z: 255., + })) +} + +fn get_rgb(o: &HashMap<String, String>) -> Result<Vector3<f32>, String> { + Ok(o.get("rgb") + .map(vector3_from_string) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(Vector3 { + x: 255., + y: 255., + z: 255., + })) +} + +fn get_f32(o: &HashMap<String, String>, tag: &str) -> Result<f32, String> { + get_f32_default(o, tag, 0.) +} + +fn get_f32_default(o: &HashMap<String, String>, tag: &str, default: f32) -> Result<f32, String> { + Ok(o.get(tag) + .map(|c| c.parse::<f32>()) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or_default()) +} + +fn get_percentage(o: &HashMap<String, String>, tag: &str) -> Result<f32, String> { + get_f32(o, tag).map(|c| c / 100.0) +} + +fn get_percentage_default( + o: &HashMap<String, String>, + tag: &str, + default: f32, +) -> Result<f32, String> { + get_f32_default(o, tag, default).map(|c| c / 100.0) +} + +#[repr(u8)] +enum Half { + X, + Y, + Z, + XM, + YM, + ZM, +} + +fn get_half(o: &HashMap<String, String>) -> Result<Half, String> { + Ok({ + let half = o + .get("half") + .map(|c| c.parse::<u8>()) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(0); + if half as usize >= mem::variant_count::<Half>() { + return Err("invalid enum".to_owned()); + } + unsafe { mem::transmute(half) } + }) +} + +pub fn load_csg( + memory_allocator: &MemoryAllocator, + input: &mut dyn Read, + name: String, +) -> Result<Vec<CSG>, String> { + let mcsg: MCSG = + from_reader(&mut Cursor::new("{").chain(input).chain(Cursor::new("}"))).unwrap(); + + mcsg.object + .iter() + .enumerate() + .map(|(i, o)| { + let name = name + "_" + o.get("name").unwrap_or(&"unknown".to_owned()); + let trs = get_trs(&o)?; + let color = get_color(&o)?; + + let cid = o + .get("cid") + .map(|c| c.parse::<usize>()) + .transpose() + .map_err(|e| e.to_string())? + .unwrap_or(0); + if o.get("type").map(|ty| &ty[..] != "csg").unwrap_or(false) { + return Err("Type unknown".to_owned()); + } + + let parts = mcsg + .csg + .get(cid) + .ok_or("unknown cid")? + .iter() + .map(|inpart| { + let ty = inpart.get("type").ok_or("no type!")?.as_str(); + let mut csgpart = CSGPart::literal(0u8.into()); + match ty { + "sphere" => { + let blend = get_f32(&inpart, "blend")?; + let shell = get_percentage(&inpart, "shell%")?; + let power = get_f32_default(&inpart, "power", 2.)?; + let rgb = get_rgb(&inpart)?; + let trs = get_trs(&inpart)?; + let half = get_half(&inpart)?; + } + _ => {} //return Err("unknown type of csg".to_owned()), + } + Ok(csgpart) + }) + .collect::<Result<Vec<CSGPart>, String>>()?; + + let parts_buffer = Buffer::from_iter( + memory_allocator, + BufferAllocateInfo { + buffer_usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + parts, + ) + .unwrap(); + + Ok(CSG { + parts: parts_buffer, + pos: Point3 { + x: 0., + y: 0., + z: 0., + }, + rot: Euler::new(Deg(0.), Deg(0.), Deg(0.)), + scale: Vector3 { + x: 1., + y: 1., + z: 1., + }, + name, + }) + }) + .collect::<Result<Vec<CSG>, String>>() } #[derive(Debug)] @@ -116,3 +398,16 @@ impl Light { } } } + +impl Default for Light { + fn default() -> Self { + Self { + pos: Point3::origin(), + colour: Vector3 { + x: 1., + y: 1., + z: 1., + }, + } + } +} diff --git a/src/primitive.mcsg b/src/primitive.mcsg new file mode 100644 index 0000000000000000000000000000000000000000..f0f6b76fa47b043e911c62cfdeb832896bad013f --- /dev/null +++ b/src/primitive.mcsg @@ -0,0 +1,941 @@ +"object" : +[ + { + "name": "objects" + "cid": "0" + "type": "csg" + "res": "64" + "color": "186 122 111" + "r": "1 0 0 0 1 0 0 0 1" + "t": "0 0 0" + } +] +"bg" : +[ +] +"csg" : +[ + //0 + [ + { + "type": "polygon" + "op": "torus" + "rgb": "247 126 92" + "blend": "0.72" + "bevel%": "0.06" + "round%": "0.69" + "n": "5" + "star%": "0.28" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-241.988 14.4664 71.172" + "s": "38.8816 38.8816 3.9999" + } + { + "type": "polygon" + "rgb": "247 126 92" + "blend": "0.72" + "bevel%": "0.06" + "round%": "0.17" + "n": "3" + "star%": "0.3" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-241.988 -169.015 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "polygon" + "rgb": "247 126 92" + "blend": "0.72" + "hole%": "0.55" + "bevel%": "0.21" + "n": "5" + "star%": "0.68" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-241.988 -79.5119 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "polygon" + "rgb": "247 126 92" + "blend": "0.72" + "cone%": "1" + "round%": "0.13" + "n": "5" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-241.988 104.438 69.3783" + "s": "34.0362 34.0362 23.3124" + } + { + "type": "polygon" + "rgb": "247 126 92" + "half": "4" + "blend": "0.72" + "shell%": "0.67" + "hole%": "0.21" + "bevel%": "0.06" + "round%": "0.69" + "n": "5" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-241.988 191.374 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "triangle" + "op": "torus" + "rgb": "180 129 187" + "blend": "0.72" + "top_v%": "1" + "top_w%": "0.43" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-153.684 14.4664 71.172" + "s": "38.8816 38.8816 3.9999" + } + { + "type": "triangle" + "rgb": "180 129 187" + "blend": "0.72" + "top_v%": "0.31" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-153.684 -169.015 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "triangle" + "rgb": "180 129 187" + "blend": "0.72" + "hole%": "0.55" + "top_v%": "0.31" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-153.684 -79.5119 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "triangle" + "rgb": "180 129 187" + "blend": "0.72" + "cone%": "1" + "top_v%": "-1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-153.684 104.438 101.94" + "s": "34.0362 34.0362 44.6027" + } + { + "type": "triangle" + "rgb": "180 129 187" + "half": "4" + "blend": "0.72" + "shell%": "0.67" + "hole%": "0.21" + "top_v%": "0.31" + "top_w%": "0.61" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-153.684 191.374 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "cube" + "op": "torus" + "rgb": "88 178 220" + "blend": "0.72" + "bevel%": "0.06" + "round%": "0.69" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-59.9187 14.4665 71.1721" + "s": "38.8816 38.8816 3.9999" + } + { + "type": "cube" + "rgb": "88 178 220" + "blend": "0.72" + "bevel%": "0.06" + "round%": "1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-59.9187 -169.015 69.3783" + "s": "40.1034 41.6193 16.4927" + } + { + "type": "polygon" + "rgb": "88 178 220" + "blend": "0.72" + "hole%": "0.55" + "bevel%": "0.21" + "n": "5" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-59.9187 -79.5119 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "cube" + "rgb": "88 178 220" + "blend": "0.72" + "cone%": "0.7" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-59.9187 104.438 103.193" + "s": "34.0362 34.0362 47.1071" + } + { + "type": "cube" + "rgb": "88 178 220" + "half": "4" + "blend": "0.72" + "shell%": "0.67" + "hole%": "0.21" + "bevel%": "0.06" + "round%": "0.69" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-59.9187 191.374 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "bezier" + "op": "torus" + "rgb": "134 193 102" + "blend": "0.72" + "bevel%": "0.06" + "round%": "0.69" + "top_v%": "0.31" + "line_w": "11" + "r": "1 0 0 0 1 0 0 0 1" + "t": "49.4242 14.4665 71.1721" + "s": "38.8816 38.8816 3.9999" + } + { + "type": "bezier" + "rgb": "134 193 102" + "blend": "0.72" + "bevel%": "0.06" + "round%": "0.17" + "line_w": "11" + "line_t": "0.77" + "cpn" : "4" + "v1": "36.7109 -82.2298 0" + "v2": "69.4205 -40.6086 0" + "v3": "-1.82643 -73.0623 0" + "r": "1 0 0 0 1 0 0 0 1" + "t": "29.5939 -167.167 69.3783" + "s": "35.6235 41.1149 16.4927" + } + { + "type": "bezier" + "rgb": "134 193 102" + "blend": "0.72" + "hole%": "0.55" + "bevel%": "0.21" + "top_v%": "0.31" + "line_w": "11" + "r": "1 0 0 0 1 0 0 0 1" + "t": "49.4242 -79.5119 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "bezier" + "rgb": "134 193 102" + "blend": "0.72" + "shell%": "0.67" + "cone%": "1" + "round%": "0.13" + "top_v%": "0.31" + "line_w": "11" + "r": "1 0 0 0 1 0 0 0 1" + "t": "49.4242 104.438 69.3783" + "s": "34.0362 34.0362 23.3124" + } + { + "type": "bezier" + "rgb": "134 193 102" + "half": "4" + "blend": "0.72" + "shell%": "0.67" + "hole%": "0.21" + "bevel%": "0.06" + "round%": "0.69" + "line_w": "25" + "cpn" : "3" + "v1": "71.036 85.1658 0" + "v2": "70.7743 8.40234 0" + "r": "1 0 0 0 1 0 0 0 1" + "t": "17.6168 171.396 69.3783" + "s": "35.518 42.5829 16.4927" + } + { + "type": "cylinder" + "op": "torus" + "rgb": "112 124 116" + "blend": "0.72" + "bevel%": "0.06" + "round%": "0.69" + "r": "1 0 0 0 1 0 0 0 1" + "t": "164.076 14.4665 71.1721" + "s": "38.8816 38.8816 3.9999" + } + { + "type": "cylinder" + "rgb": "112 124 116" + "blend": "0.72" + "bevel%": "0.06" + "round%": "1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "164.076 -169.015 69.3783" + "s": "34.0362 34.0362 101.907" + } + { + "type": "cylinder" + "rgb": "112 124 116" + "blend": "0.72" + "hole%": "0.55" + "bevel%": "0.21" + "r": "1 0 0 0 1 0 0 0 1" + "t": "164.076 -79.5118 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "cylinder" + "rgb": "112 124 116" + "blend": "0.72" + "shell%": "0.67" + "cone%": "1" + "round%": "0.13" + "r": "1 0 0 0 1 0 0 0 1" + "t": "164.076 104.438 107.804" + "s": "34.0362 34.0362 50.9307" + } + { + "type": "cylinder" + "rgb": "112 124 116" + "half": "4" + "blend": "0.72" + "shell%": "0.67" + "hole%": "0.21" + "bevel%": "0.06" + "round%": "0.69" + "r": "1 0 0 0 1 0 0 0 1" + "t": "164.076 191.374 69.3783" + "s": "34.0362 34.0362 16.4927" + } + { + "type": "sphere" + "rgb": "249 191 69" + "blend": "0.72" + "r": "1 0 0 0 1 0 0 0 1" + "t": "277.566 -70.0318 94.032" + "s": "38.8816 37.8267 81.005" + } + { + "type": "sphere" + "rgb": "249 191 69" + "half": "4" + "blend": "0.72" + "shell%": "0.67" + "r": "1 0 0 0 1 0 0 0 1" + "t": "277.566 -177.452 71.8416" + "s": "53.888 53.888 53.888" + } + { + "type": "jt" + "rgb": "225 166 121" + "blend": "0.72" + "line_w": "16" + "line_t": "0.42" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "283.517 107.874 88.6313" + "s": "16 33.3182 16" + } + { + "name": "polygon" + "type": "polygon" + "rgb": "247 126 92" + "blend": "0.72" + "bevel%": "0.05" + "round%": "0.11" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-241.988 -259.969 69.3783" + "s": "34.0362 34.0362 31.8128" + } + { + "name": "triangle" + "type": "triangle" + "rgb": "180 129 187" + "blend": "0.72" + "top_v%": "-1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-147.505 -263.066 69.3783" + "s": "32.5919 33.7365 14.9794" + } + { + "type": "bezier" + "rgb": "134 193 102" + "blend": "0.72" + "line_w": "11" + "line_t": "0.86" + "cpn" : "4" + "cp3d" : "1" + "v1": "90.7305 -51.0184 -46.0015" + "v2": "56.2318 -0.434326 -31.4712" + "v3": "36.103 -119.806 -16.6211" + "r": "1 0 0 0 1 0 0 0 1" + "t": "15.9159 -293.365 66.177" + "s": "45.3652 59.903 34.0007" + } + { + "type": "cylinder" + "rgb": "93 172 129" + "blend": "0.72" + "cone%": "0.68" + "bevel%": "0.06" + "round%": "0.06" + "r": "1 0 0 0 1 0 0 0 1" + "t": "169.337 -245.935 52.9717" + "s": "48.8821 27.5035 60.9762" + } + { + "type": "cube" + "rgb": "88 178 220" + "blend": "0.72" + "bevel%": "1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-76.329 -271.519 69.3783" + "s": "16.7095 42.7063 4.59684" + } + { + "type": "cylinder" + "rgb": "93 172 129" + "blend": "0.72" + "hole%": "0.53" + "bevel%": "0.06" + "round%": "0.34" + "r": "1 0 0 0 1 0 0 0 1" + "t": "174.848 -318.212 52.9717" + "s": "48.8821 27.5035 29.1618" + } + { + "type": "cylinder" + "rgb": "93 172 129" + "blend": "0.72" + "hole%": "0.53" + "bevel%": "0.06" + "round%": "0.34" + "r": "1 0 0 0 1 0 0 0 1" + "t": "288.785 -320.758 52.9717" + "s": "48.8821 27.5035 6.8466" + } + { + "type": "cube" + "op": "torus" + "rgb": "88 178 220" + "blend": "0.72" + "bevel%": "1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-33.9907 -271.519 69.3783" + "s": "19.6374 42.7063 4.7077" + } + { + "type": "sphere" + "rgb": "249 191 69" + "blend": "0.72" + "power": "2.90" + "r": "1 0 0 0 1 0 0 0 1" + "t": "277.566 16.3762 94.032" + "s": "38.8816 37.8267 81.005" + } + { + "type": "jt" + "op": "revolve" + "rgb": "225 166 121" + "blend": "0.72" + "line_w": "31" + "line_t": "0.89" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "278.166 205.342 88.6313" + "s": "31 33.3182 31" + } + { + "type": "jt" + "rgb": "225 166 121" + "blend": "0.72" + "hole%": "0.61" + "line_w": "16" + "line_t": "0.42" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "403.632 114.486 88.6313" + "s": "16 33.3182 16" + } + { + "type": "jt" + "rgb": "225 166 121" + "blend": "0.72" + "cone%": "0.56" + "line_w": "16" + "line_t": "0.42" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "396.285 208.698 88.6313" + "s": "16 33.3182 16" + } + { + "type": "triangle" + "rgb": "219 121 142" + "lmy": "1" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-325.353 184.805 58.3003" + "s": "23.7624 35.625 13.72" + } + { + "type": "triangle" + "rgb": "219 121 142" + "hole%": "0.55" + "lmy": "1" + "bevel": "4.28" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-325.353 92.8812 58.3003" + "s": "23.7624 35.625 13.72" + } + { + "type": "triangle" + "rgb": "219 121 142" + "cone%": "0.71" + "hole%": "0.55" + "lmy": "1" + "bevel": "4.28" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-325.353 7.93176 58.3003" + "s": "23.7624 35.625 13.72" + } + { + "type": "polygon" + "op": "lmz" + "rgb": "225 121 121" + "blend": "0.54" + "cone%": "1" + "hole%": "0.61" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "394.82 -57.0846 84.193" + "s": "40.4477 40.4477 55.7464" + } + { + "type": "cylinder" + "op": "lmz" + "rgb": "225 121 121" + "blend": "0.52" + "cone%": "1" + "hole%": "0.61" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "394.006 -159.784 84.193" + "s": "38.7176 38.7176 53.3619" + } + { + "type": "bezier" + "op": "lmz" + "rgb": "225 121 121" + "blend": "0.44" + "cone%": "1" + "hole%": "0.61" + "line_w": "19" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "396.131 -259.546 84.193" + "s": "32.5422 32.5422 15.715" + } + { + "type": "triangle" + "op": "lmz" + "rgb": "225 121 121" + "blend": "0.54" + "cone%": "1" + "hole%": "0.61" + "lmy": "1" + "r": "-0.0348995 -0.999391 0 0.999391 -0.0348995 0 0 0 1" + "t": "383.098 28.0329 84.193" + "s": "40.4477 55.6998 77.4739" + } + { + "type": "jt" + "op": "revolve" + "rgb": "225 166 121" + "blend": "0.72" + "taper_f": "1" + "line_w": "31" + "line_t": "0.89" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "275.387 309.296 87.3653" + "s": "31 33.3182 31" + } + { + "type": "jt" + "op": "revolve" + "rgb": "225 166 121" + "blend": "0.72" + "taper_f": "1" + "taper_c": "1" + "line_w": "31" + "line_t": "0.89" + "r": "0.48481 -0.87462 0 0.87462 0.48481 0 0 0 1" + "t": "382.569 304.773 88.6313" + "s": "31 66.5444 31" + } + { + "type": "bezier" + "op": "revolve" + "rgb": "152 121 225" + "blend": "1.00" + "line_w": "3" + "cpn" : "3" + "v1": "46.7568 13.6572 0" + "v2": "24.5674 112.006 0" + "r": "1 0 0 0 0 1 0 -1 0" + "t": "-327.203 -77.172 11.9998" + "s": "23.3784 56.003 23.3784" + } + { + "type": "bezier" + "op": "revolve" + "rgb": "152 121 225" + "blend": "1.00" + "line_w": "3" + "cpn" : "4" + "v1": "47.1742 1.97522 0" + "v2": "29.0068 67.8888 0" + "v3": "73.7265 131.722 0" + "r": "1 0 0 0 0 1 0 -1 0" + "t": "-341.023 -208.021 11.9997" + "s": "36.8633 65.861 36.8633" + } + { + "type": "triangle" + "rgb": "186 122 111" + "blend": "0.65" + "bevel": "8.39" + "cpn" : "4" + "cp3d" : "1" + "v1": "123.934 -5.87432 -9.9001" + "v2": "31.9969 101.067 13.4463" + "v3": "66.3762 47.0197 99.3701" + "r": "0.224951 0.97437 0 -0.97437 0.224951 0 0 0 1" + "t": "-143.025 288.653 33.8015" + "s": "70.357 61.8607 63.0251" + } + { + "type": "triangle" + "rgb": "186 122 111" + "blend": "1.00" + "round": "5.00" + "bevel": "12.00" + "cpn" : "4" + "v1": "115.848 40.3154 0" + "v2": "87.1635 72.8789 0" + "v3": "9.2695 87.1677 0" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-381.523 275.152 36.1639" + "s": "69.924 55.5839 14.7019" + } + { + "type": "line" + "op": "revolve" + "rgb": "152 121 225" + "blend": "1.00" + "line_w": "3" + "cpn" : "4" + "v1": "47.1742 1.97522 0" + "v2": "68.3307 65.6229 0" + "v3": "55.2117 86.2898 0" + "r": "1 0 0 0 0 1 0 -1 0" + "t": "-332.493 -367.087 11.9997" + "s": "34.1654 43.1449 34.1654" + } + { + "type": "line" + "rgb": "134 193 102" + "blend": "0.72" + "line_w": "11" + "line_t": "0.86" + "cpn" : "4" + "cp3d" : "1" + "v1": "103.259 -27.1136 -27.6716" + "v2": "33.445 -25.9005 -45.0142" + "v3": "43.2235 -103.052 -8.07178" + "r": "1 0 0 0 1 0 0 0 1" + "t": "-85.6751 -330.22 66.177" + "s": "51.6295 51.526 33.5071" + } + { + "type": "cube" + "op": "lmz" + "rgb": "186 122 111" + "blend": "0.35" + "cone%": "0.92" + "bevel%": "0.167615" + "r": "1 0 0 0 1 0 0 0 1" + "t": "103.307 355.669 66.3865" + "s": "37.9821 57.3678 56.5561" + } + { + "type": "triangle" + "rgb": "186 122 111" + "blend": "0.65" + "bevel": "8.39" + "cpn" : "3" + "cp3d" : "1" + "v1": "101.186 -19.5941 -38.654" + "v2": "155.111 15.4358 97.1493" + "r": "0.224951 0.97437 0 -0.97437 0.224951 0 0 0 1" + "t": "-32.2507 286.425 62.5554" + "s": "85.9455 25.9049 76.2916" + } + ] +] +"palette" : +[ + "0 0 0" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "215 84 85" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "247 92 47" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "253 160 149" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "134 193 102" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "102 186 183" + "38 135 133" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "129 199 212" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "88 90 141" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "224 131 174" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "221 221 221" + "198 198 198" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "75 75 75" + "68 68 68" + "75 75 75" + "0 0 0" +]