initial mcsg decode

This commit is contained in:
John Hunter
2023-02-05 20:12:11 +00:00
parent 7828f0ee0b
commit bed905725b
8 changed files with 1843 additions and 17 deletions
+45
View File
@@ -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}"
}
]
}
Generated
+9
View File
@@ -721,6 +721,9 @@ dependencies = [
"egui_winit_vulkano", "egui_winit_vulkano",
"obj", "obj",
"rodio", "rodio",
"serde",
"serde_json",
"utf-8",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"vulkano-util", "vulkano-util",
@@ -2212,6 +2215,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.2" version = "0.8.2"
+4
View File
@@ -33,6 +33,10 @@ egui_winit_vulkano = "0.22"
ash = "*" ash = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
utf-8 = "*"
# using latest gits # using latest gits
[patch.crates-io] [patch.crates-io]
+3 -1
View File
@@ -4,7 +4,7 @@ use egui::{
}; };
use egui_winit_vulkano::Gui; 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) { fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) {
ui.label(egui::RichText::new(text).size(size)); ui.label(egui::RichText::new(text).size(size));
@@ -17,6 +17,7 @@ pub struct GState {
pub meshes: Vec<Mesh>, pub meshes: Vec<Mesh>,
pub lights: Vec<Light>, pub lights: Vec<Light>,
pub csg: Vec<CSG>,
pub fps: [f64; 128], pub fps: [f64; 128],
} }
@@ -29,6 +30,7 @@ impl Default for GState {
meshes: vec![], meshes: vec![],
lights: vec![], lights: vec![],
csg: vec![],
fps: [0.0; 128], fps: [0.0; 128],
} }
+20 -6
View File
@@ -1,3 +1,4 @@
#![feature(variant_count)]
use cgmath::{Deg, EuclideanSpace, Euler, Matrix3, Matrix4, Point3, Rad, SquareMatrix, Vector3}; use cgmath::{Deg, EuclideanSpace, Euler, Matrix3, Matrix4, Point3, Rad, SquareMatrix, Vector3};
use std::io::Cursor; use std::io::Cursor;
use std::{sync::Arc, time::Instant}; use std::{sync::Arc, time::Instant};
@@ -18,7 +19,7 @@ use vulkano::pipeline::graphics::vertex_input::Vertex;
use vulkano::pipeline::PipelineBindPoint; use vulkano::pipeline::PipelineBindPoint;
use vulkano::shader::{ShaderModule, SpecializationConstants}; use vulkano::shader::{ShaderModule, SpecializationConstants};
use vulkano::swapchain::{PresentMode, SwapchainPresentInfo}; use vulkano::swapchain::{PresentMode, SwapchainPresentInfo};
use vulkano::{Version, VulkanLibrary}; use vulkano::VulkanLibrary;
use winit::event::{DeviceEvent, ElementState, MouseButton, VirtualKeyCode}; use winit::event::{DeviceEvent, ElementState, MouseButton, VirtualKeyCode};
use egui_winit_vulkano::Gui; use egui_winit_vulkano::Gui;
@@ -58,6 +59,7 @@ mod gui;
use crate::gui::*; use crate::gui::*;
mod objects; mod objects;
use crate::objects::*; use crate::objects::*;
mod mcsg_deserialise;
pub type MemoryAllocator = StandardMemoryAllocator; pub type MemoryAllocator = StandardMemoryAllocator;
@@ -370,11 +372,23 @@ fn main() {
d: false, d: false,
}; };
gstate.meshes.push(load_obj( gstate.meshes.push(
&memory_allocator, load_obj(
&mut Cursor::new(PLATONIC_SOLIDS[0].1), &memory_allocator,
PLATONIC_SOLIDS[0].0.to_string(), &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 gstate
.lights .lights
+516
View File
@@ -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)
}
}
+305 -10
View File
@@ -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 bytemuck::{Pod, Zeroable};
use cgmath::{Deg, Euler, Point3, Vector3}; use cgmath::{Deg, EuclideanSpace, Euler, Matrix3, Point3, SquareMatrix, Vector3};
use obj::{LoadConfig, ObjData}; use obj::{LoadConfig, ObjData, ObjError};
use serde::{Deserialize, Serialize};
use vulkano::{ use vulkano::{
buffer::{Buffer, BufferAllocateInfo, BufferUsage, Subbuffer}, buffer::{Buffer, BufferAllocateInfo, BufferUsage, Subbuffer},
half::f16,
pipeline::graphics::vertex_input::Vertex, 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 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 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 // 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 indices: Subbuffer<[u32]>,
pub pos: Point3<f32>, pub pos: Point3<f32>,
pub rot: Euler<Deg<f32>>, pub rot: Euler<Deg<f32>>,
pub scale: f32, pub scale: Vector3<f32>,
} }
pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name: String) -> Mesh { #[derive(Debug)]
let object = ObjData::load_buf_with_config(input, LoadConfig::default()).unwrap(); pub struct CSG {
pub name: String,
pub parts: Subbuffer<[CSGPart]>,
pub pos: Point3<f32>,
pub rot: Euler<Deg<f32>>,
pub scale: Vector3<f32>,
}
#[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![]; let mut vertices = vec![];
@@ -87,7 +141,7 @@ pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name:
) )
.unwrap(); .unwrap();
Mesh { Ok(Mesh {
vertices: vertex_buffer, vertices: vertex_buffer,
indices: index_buffer, indices: index_buffer,
pos: Point3 { pos: Point3 {
@@ -96,9 +150,237 @@ pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name:
z: 0., z: 0.,
}, },
rot: Euler::new(Deg(0.), Deg(0.), Deg(0.)), rot: Euler::new(Deg(0.), Deg(0.), Deg(0.)),
scale: 1., scale: Vector3 {
x: 1.,
y: 1.,
z: 1.,
},
name, 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)] #[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.,
},
}
}
}
+941
View File
@@ -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"
]