diff options
-rw-r--r-- | gen_random/src/lib.rs | 143 | ||||
-rw-r--r-- | gen_random_proc_macro/src/lib.rs | 93 | ||||
-rw-r--r-- | src/fshader_main.glsl | 5 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/sdf.rs | 63 |
5 files changed, 171 insertions, 134 deletions
diff --git a/gen_random/src/lib.rs b/gen_random/src/lib.rs index 07da6b5..4bd67be 100644 --- a/gen_random/src/lib.rs +++ b/gen_random/src/lib.rs @@ -4,12 +4,28 @@ use std::rc::Rc; use std::sync::Arc; use std::cell::{Cell, RefCell}; +pub trait GenRandomParams: Sized + Default + Copy { + fn inc_depth(self) -> Self; +} + +impl GenRandomParams for () { + fn inc_depth(self) -> Self { + () + } +} + +impl GenRandomParams for i64 { + fn inc_depth(self) -> Self { + self - 1 + } +} + /// Generate random structs and enums! /// /// You don't need to implement this trait yourself — instead, use the `derive` macro: /// ``` /// use gen_random_proc_macro::GenRandom; -/// use gen_random::GenRandom; +/// use gen_random::{GenRandom, GenRandomParams}; /// /// #[derive(GenRandom, Debug)] /// enum MyType { @@ -36,132 +52,125 @@ use std::cell::{Cell, RefCell}; /// } /// ``` -pub trait GenRandom: Sized { - /// To allow recursive structs like binary trees, - /// we provide a `max_depth` functionality. - /// If your struct isn't recursive, you can use [GenRandom::gen_random] instead. - /// If `max_depth <= 0` the **first** variant of an `enum` is always chosen - /// (so make sure `Empty` or whatever comes first). - /// For `Option<T>`, if `max_depth <= 0`, `None` is always chosen. - fn gen_random_max_depth(rng: &mut impl Rng, max_depth: isize) -> Self; +pub trait GenRandom<Params: GenRandomParams>: Sized { + /// Generate a random value with parameters. + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self; - /// Generate a random instance of this struct using the given random number generator. + /// Generate a random value fn gen_random(rng: &mut impl Rng) -> Self { - Self::gen_random_max_depth(rng, isize::MAX) + Self::gen_random_params(rng, Params::default()) } - /// Generate a random instance of this struct using `rand::thread_rng()` with a maximum depth. - fn gen_thread_random_max_depth(max_depth: isize) -> Self { + /// Generate a random instance of this struct using `rand::thread_rng()` with parameters. + fn gen_thread_random_params(params: Params) -> Self { let mut thread_rng = rand::thread_rng(); - Self::gen_random_max_depth(&mut thread_rng, max_depth) + Self::gen_random_params(&mut thread_rng, params) } /// Generate a random instance of this struct using `rand::thread_rng()`. fn gen_thread_random() -> Self { - Self::gen_thread_random_max_depth(isize::MAX) + Self::gen_thread_random_params(Params::default()) } } -pub fn gen_random_vec<T: GenRandom>(rng: &mut impl Rng, len: usize) -> Vec<T> { +pub fn gen_random_vec<P: GenRandomParams, T: GenRandom<P>>(rng: &mut impl Rng, len: usize) -> Vec<T> { (0..len).map(|_| T::gen_random(rng)).collect() } -pub fn gen_thread_random_vec<T: GenRandom>(len: usize) -> Vec<T> { +pub fn gen_thread_random_vec<P: GenRandomParams, T: GenRandom<P>>(len: usize) -> Vec<T> { gen_random_vec(&mut rand::thread_rng(), len) } -impl GenRandom for f32 { - fn gen_random_max_depth(rng: &mut impl Rng, _depth: isize) -> Self { +impl<Params: GenRandomParams> GenRandom<Params> for f32 { + fn gen_random_params(rng: &mut impl Rng, _params: Params) -> Self { rng.gen_range(0.0..1.0) } } -impl GenRandom for f64 { - fn gen_random_max_depth(rng: &mut impl Rng, _depth: isize) -> Self { +impl<Params: GenRandomParams> GenRandom<Params> for f64 { + fn gen_random_params(rng: &mut impl Rng, _params: Params) -> Self { rng.gen_range(0.0..1.0) } } -impl<T: GenRandom> GenRandom for Box<T> { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - Box::new(T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for Box<T> { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + Box::new(T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for [T; 1] { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - [T::gen_random_max_depth(rng, depth)] +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for [T; 1] { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + [T::gen_random_params(rng, params)] } } -impl<T: GenRandom> GenRandom for [T; 2] { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - [T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)] +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for [T; 2] { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + [T::gen_random_params(rng, params), T::gen_random_params(rng, params)] } } -impl<T: GenRandom> GenRandom for [T; 3] { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - [T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)] +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for [T; 3] { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + [T::gen_random_params(rng, params), T::gen_random_params(rng, params), T::gen_random_params(rng, params)] } } -impl<T: GenRandom> GenRandom for [T; 4] { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - [T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)] +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for [T; 4] { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + [T::gen_random_params(rng, params), T::gen_random_params(rng, params), T::gen_random_params(rng, params), T::gen_random_params(rng, params)] } } -impl<T: GenRandom> GenRandom for (T, T) { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - (T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for (T, T) { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + (T::gen_random_params(rng, params), T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for (T, T, T) { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - (T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for (T, T, T) { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + (T::gen_random_params(rng, params), T::gen_random_params(rng, params), T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for (T, T, T, T) { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - (T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth), T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for (T, T, T, T) { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + (T::gen_random_params(rng, params), T::gen_random_params(rng, params), T::gen_random_params(rng, params), T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for Rc<T> { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - Self::new(T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for Rc<T> { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + Self::new(T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for Arc<T> { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - Self::new(T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for Arc<T> { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + Self::new(T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for Cell<T> { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - Self::new(T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for Cell<T> { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + Self::new(T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for RefCell<T> { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - Self::new(T::gen_random_max_depth(rng, depth)) +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for RefCell<T> { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + Self::new(T::gen_random_params(rng, params)) } } -impl<T: GenRandom> GenRandom for Option<T> { - fn gen_random_max_depth(rng: &mut impl Rng, depth: isize) -> Self { - if depth <= 0 { - None - } else if rng.gen_range(0..2) == 0 { +impl<Params: GenRandomParams, T: GenRandom<Params>> GenRandom<Params> for Option<T> { + fn gen_random_params(rng: &mut impl Rng, params: Params) -> Self { + if rng.gen_range(0..2) == 0 { None } else { - Some(T::gen_random_max_depth(rng, depth)) + Some(T::gen_random_params(rng, params)) } } } @@ -171,7 +180,7 @@ impl<T: GenRandom> GenRandom for Option<T> { mod tests { extern crate gen_random_proc_macro; extern crate rand; - use super::{gen_thread_random_vec, GenRandom}; + use super::{gen_thread_random_vec, GenRandom, GenRandomParams}; use gen_random_proc_macro::GenRandom; #[derive(GenRandom, Debug)] @@ -202,10 +211,12 @@ mod tests { } #[derive(GenRandom, Debug)] + #[params(i64)] enum BinaryTree { #[prob(1)] Empty, #[prob(99)] + #[only_if(params >= 0)] Node(f64, Box<BinaryTree>, Box<BinaryTree>) } @@ -252,8 +263,8 @@ mod tests { } #[test] - fn binary_tree_max_depth() { - let bintree = BinaryTree::gen_thread_random_max_depth(5); + fn binary_tree_params() { + let bintree = BinaryTree::gen_thread_random_params(5); println!("{bintree:?}"); } } diff --git a/gen_random_proc_macro/src/lib.rs b/gen_random_proc_macro/src/lib.rs index 9b7a771..961b2a0 100644 --- a/gen_random_proc_macro/src/lib.rs +++ b/gen_random_proc_macro/src/lib.rs @@ -10,7 +10,7 @@ use std::any::type_name; use quote::quote; /// See `gen_random::GenRandom`. -#[proc_macro_derive(GenRandom, attributes(prob, scale, bias))] +#[proc_macro_derive(GenRandom, attributes(prob, scale, bias, params, only_if))] pub fn gen_random_derive(input: TokenStream) -> TokenStream { // Construct a representation of Rust code as a syntax tree // that we can manipulate @@ -19,7 +19,7 @@ pub fn gen_random_derive(input: TokenStream) -> TokenStream { impl_gen_random(&ast) } -fn get_attribute(attrs: &[syn::Attribute], name: &str) -> Option<proc_macro2::TokenStream> { +fn get_attribute(attrs: &[syn::Attribute], name: &str) -> Option<TokenStream2> { let attr = attrs.iter().find(|a| { let path = &a.path; if let Some(ident) = path.get_ident() { @@ -56,14 +56,14 @@ fn parse_attribute_value<T: FromStr>(attrs: &[syn::Attribute], name: &str) -> Op Some(value) } -fn generate_fields(fields: &syn::Fields) -> impl quote::ToTokens { +fn generate_fields(fields: &syn::Fields, params_type: &TokenStream2) -> impl quote::ToTokens { let mut field_values = quote! {}; for field in fields.iter() { if let Some(name) = &field.ident { field_values.extend(quote! {#name: }); } let ty = &field.ty; - field_values.extend(quote! { <#ty as GenRandom>::gen_random_max_depth(rng, _depth - 1) }); + field_values.extend(quote! { <#ty as GenRandom<#params_type>>::gen_random_params(rng, <#params_type as GenRandomParams>::inc_depth(params)) }); if let Some(scale) = get_attribute(&field.attrs, "scale") { field_values.extend(quote! { * ( #scale ) }); @@ -93,95 +93,64 @@ fn generate_fields(fields: &syn::Fields) -> impl quote::ToTokens { } } -// very very precise summation algorithm -// see https://en.wikipedia.org/wiki/Kahan_summation_algorithm -fn kahan_sum(it: impl IntoIterator<Item = f64>) -> f64 { - let mut it = it.into_iter(); - let mut sum = 0.0; - let mut c = 0.0; - while let Some(x) = it.next() { - let y = x - c; - let t = sum + y; - c = (t - sum) - y; - sum = t; - } - sum -} - fn impl_gen_random(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let mut function_body; + let params_type = get_attribute(&ast.attrs, "params").unwrap_or(quote! { () }); match &ast.data { syn::Data::Enum(enumeration) => { let variants = &enumeration.variants; + function_body = quote! { + let mut prob_sum = 0.0; + }; - let prob_sum = kahan_sum(variants.iter().map(|variant| { - match parse_attribute_value(&variant.attrs, "prob") { + for variant in variants.iter() { + match parse_attribute_value::<f64>(&variant.attrs, "prob") { Some(prob) => if prob >= 0.0 { - prob + let only_if = get_attribute(&variant.attrs, "only_if") + .unwrap_or(quote! { true }); + + function_body.extend(quote! { + if #only_if { prob_sum += #prob; } + }); } else { panic!("Variant {} has negative probability", variant.ident) }, None => panic!("Variant {} has no probability", variant.ident) } - })); - - if prob_sum <= f64::EPSILON { - panic!("Sum of probabilties is (basically) zero."); } - // ideally we would just do - // let mut variant: f64 = rng.gen_range(0.0..prob_sum); - // variant -= variant1_probability; - // if variant < 0.0 { bla bla bla } - // variant -= variant2_probability; - // if variant < 0.0 { bla bla bla } - // etc. - // but because of floating point imprecision, it's possible - // that all if conditions are false. - // however we know that for each subtraction at most one ULP is lost. - // so we'll be fine as long as we put the end of the range at - // prob_sum * (1.0 - (variant_count + 2) * ULP) - // the + 2 is for the imprecision lost in kahan_sum and one more just to be sure. - - let variant_max = prob_sum * (1.0 - f64::EPSILON * (variants.len() + 2) as f64); - function_body = quote! { - let mut variant: f64 = rng.gen_range(0.0..=#variant_max); - }; - - // this test value ensures that the gen_random function never panicks. - let mut test_variant = variant_max; + let compensation = (variants.len() + 1) as f64 * f64::EPSILON; + function_body.extend(quote! { + let mut variant: f64 = rng.gen_range(0.0..prob_sum - #compensation); + }); // parse enum fields for variant in variants.iter() { // Note: None case was checked above when computing prob_sum let probability: f64 = parse_attribute_value(&variant.attrs, "prob").unwrap(); + let only_if = get_attribute(&variant.attrs, "only_if") + .unwrap_or(quote! { true }); let name = &variant.ident; - let field_values = generate_fields(&variant.fields); + let field_values = generate_fields(&variant.fields, ¶ms_type); function_body.extend(quote! { - variant -= #probability; - // note: if _depth <= 0, we will always return the first variant. - if _depth <= 0 || variant < 0.0 { return Self::#name #field_values; } + if #only_if { + variant -= #probability; + if variant < 0.0 { return Self::#name #field_values; } + } }); - test_variant -= probability; - - - } - - if test_variant >= 0.0 { - panic!("i did floating-point math wrong. this should never happen. (test_variant = {test_variant})"); } function_body.extend(quote! { - panic!("RNG returned value outside of range.") + panic!("RNG returned value outside of range (this should never happen).") }); }, syn::Data::Struct(structure) => { - let field_values = generate_fields(&structure.fields); + let field_values = generate_fields(&structure.fields, ¶ms_type); function_body = quote! { Self #field_values }; @@ -190,8 +159,8 @@ fn impl_gen_random(ast: &syn::DeriveInput) -> TokenStream { }; let gen = quote! { - impl GenRandom for #name { - fn gen_random_max_depth(rng: &mut impl rand::Rng, _depth: isize) -> Self { + impl GenRandom<#params_type> for #name { + fn gen_random_params(rng: &mut impl rand::Rng, params: #params_type) -> Self { #function_body } } diff --git a/src/fshader_main.glsl b/src/fshader_main.glsl index 9e9334e..116fcd7 100644 --- a/src/fshader_main.glsl +++ b/src/fshader_main.glsl @@ -37,13 +37,12 @@ vec3 hsv_to_rgb(vec3 hsv) { vec3 get_color(vec3 p) { if (u_hsv != 0) { - vec3 hsv = get_color_(p); + vec3 hsv = clamp(get_color_(p), 0.0, 1.0); // make sure object isn't too dark so we can actually see it hsv.z = mix(hsv.z, 1.0, 0.5); return hsv_to_rgb(hsv); } else { - // we're not clamping this because it makes a cool glowing effect if we don't - vec3 color = get_color_(p); + vec3 color = clamp(get_color_(p), 0.0, 1.0); return mix(color, vec3(1.0), 0.2); } } diff --git a/src/main.rs b/src/main.rs index f77a3b7..598a12a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -152,6 +152,7 @@ impl Programs { let source_main = include_str!("fshader_main.glsl"); let source_test = include_str!("fshader_test.glsl"); let source_common = include_str!("fshader_common.glsl"); + println!("{:?}",scene.sdf); let mut sdf = String::new(); let mut get_color = String::new(); @@ -3,7 +3,7 @@ extern crate rand; extern crate serde; extern crate serde_cbor; -use gen_random::GenRandom; +use gen_random::{GenRandom, GenRandomParams}; use gen_random_proc_macro::GenRandom; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -15,8 +15,31 @@ macro_rules! write_str { ($( $arg:tt )*) => { write!($($arg)*).unwrap() } } +#[derive(Copy, Clone)] +pub struct SdfParams { + max_depth: i32 +} + +impl Default for SdfParams { + fn default() -> Self { + Self { + max_depth: 5 + } + } +} + +impl GenRandomParams for SdfParams { + fn inc_depth(self) -> Self { + Self { + max_depth: self.max_depth - 1, + ..self + } + } +} + /// these are constant across 3D space, not across time/user input/etc. #[derive(Debug, GenRandom, Serialize, Deserialize)] +#[params(SdfParams)] pub enum Constant { #[prob(0.0)] F32(f32), @@ -115,6 +138,7 @@ impl Display for Constant { } #[derive(GenRandom, Debug, Serialize, Deserialize)] +#[params(SdfParams)] pub struct Constant3(Constant, Constant, Constant); impl std::ops::Add<f32> for Constant3 { @@ -145,10 +169,12 @@ impl Display for Constant3 { } #[derive(GenRandom, Debug, Serialize, Deserialize)] +#[params(SdfParams)] pub enum R3ToR3 { #[prob(0)] Identity, #[prob(6)] + #[only_if(params.max_depth >= 0)] Compose(Box<R3ToR3>, Box<R3ToR3>), #[prob(1)] Translate(Constant3), @@ -169,21 +195,26 @@ pub enum R3ToR3 { #[prob(2)] #[bias(0.01)] Sigmoid, //based on sigmoid(x) = 1 / (1 + e^-x) + #[prob(2)] + Twisty, } // note : i dont think R → R transformations really accomplish that much // that can't be done with R³ → R³. #[derive(GenRandom, Debug, Serialize, Deserialize)] +#[params(SdfParams)] pub enum RToR { #[prob(1)] Identity, #[prob(0)] + #[only_if(params.max_depth >= 0)] Compose(Box<RToR>, Box<RToR>), #[prob(0)] Subtract(Constant), } #[derive(GenRandom, Debug, Serialize, Deserialize)] +#[params(SdfParams)] pub enum R3ToR { #[prob(1)] Sphere(Constant), @@ -204,12 +235,16 @@ pub enum R3ToR { thickness: Constant, }, #[prob(8)] + #[only_if(params.max_depth >= 0)] Compose(Box<R3ToR3>, Box<R3ToR>, Box<RToR>), #[prob(4)] + #[only_if(params.max_depth >= 0)] Mix(Box<R3ToR>, Box<R3ToR>, Constant), #[prob(2)] + #[only_if(params.max_depth >= 0)] SmoothMin(Box<R3ToR>, Box<R3ToR>), #[prob(2)] + #[only_if(params.max_depth >= 0)] Min(Box<R3ToR>, Box<R3ToR>), } @@ -316,7 +351,7 @@ impl fmt::Display for GLSLType { } } -trait Function: Sized + Default + GenRandom + ImportExport { +trait Function: Sized + Default + GenRandom<SdfParams> + ImportExport { /// appends `code` with glsl code to apply the function to the input variable. /// returns the output variable. #[must_use] @@ -344,9 +379,12 @@ trait Function: Sized + Default + GenRandom + ImportExport { fn good_random(rng: &mut impl Rng, function_length: usize) -> Self { let default_len = Self::default().export_string().len(); for max_depth in 1.. { + let params = SdfParams { + max_depth + }; let mut functions = vec![]; for _i in 0..20 { - let f = Self::gen_random_max_depth(rng, max_depth); + let f = Self::gen_random_params(rng, params); let len = f.export_string().len().saturating_sub(default_len); functions.push((len, f)); } @@ -485,6 +523,25 @@ impl Function for R3ToR3 { ); output } + Twisty => { + let a = var.next(); + let theta = var.next(); + let output = var.next(); + write_str!( + code, + "vec2 {a} = vec2(cos({input}.x), sin({input}.y));\n" + ); + write_str!( + code, + "float {theta} = {input}.z * sqrt(2.0);\n" + ); + write_str!( + code, + "vec3 {output} = vec3({a}.x*cos({theta})+{a}.y*sin({theta}), + {a}.y*cos({theta})-{a}.x*sin({theta}),{input}.z) * (1.0/4.0);\n" + ); + output + } } } } |