initial mcsg decode
This commit is contained in:
Vendored
+45
@@ -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
@@ -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"
|
||||
|
||||
@@ -33,6 +33,10 @@ egui_winit_vulkano = "0.22"
|
||||
|
||||
ash = "*"
|
||||
|
||||
serde = { version = "*", features = ["derive"] }
|
||||
serde_json = "*"
|
||||
|
||||
utf-8 = "*"
|
||||
|
||||
# using latest gits
|
||||
[patch.crates-io]
|
||||
|
||||
+3
-1
@@ -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],
|
||||
}
|
||||
|
||||
+17
-3
@@ -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(
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+304
-9
@@ -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>,
|
||||
}
|
||||
|
||||
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();
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[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.,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
Reference in New Issue
Block a user