summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-12-15 11:33:32 -0500
committerpommicket <pommicket@gmail.com>2022-12-15 11:33:32 -0500
commit4f48f02f91f2134b4452729dd96524ea1814efb2 (patch)
tree2af6a435a33119bb2169cdec48da7a71c6bf0437
parente45cbe4744c76a160e012a4d9bcf8d46340821d8 (diff)
various fixes
-rw-r--r--gen_random/src/lib.rs36
-rw-r--r--gen_random_proc_macro/src/lib.rs38
-rw-r--r--src/main.rs13
-rw-r--r--src/sdf.rs114
4 files changed, 133 insertions, 68 deletions
diff --git a/gen_random/src/lib.rs b/gen_random/src/lib.rs
index c21a1b1..07da6b5 100644
--- a/gen_random/src/lib.rs
+++ b/gen_random/src/lib.rs
@@ -6,7 +6,7 @@ use std::cell::{Cell, RefCell};
/// Generate random structs and enums!
///
-/// You shouldn't implement this trait yourself — instead, use the `derive` macro:
+/// You don't need to implement this trait yourself — instead, use the `derive` macro:
/// ```
/// use gen_random_proc_macro::GenRandom;
/// use gen_random::GenRandom;
@@ -14,15 +14,15 @@ use std::cell::{Cell, RefCell};
/// #[derive(GenRandom, Debug)]
/// enum MyType {
/// // this variant will be chosen 7 / 10.5 = 2/3 of the time
-/// #[prob = 7]
+/// #[prob(7)]
/// Variant1(f64),
/// // this variant will be chosen 3.5 / 10.5 = 1/3 of the time
-/// #[prob = 3.5]
+/// #[prob(3.5)]
/// Variant2 {
/// // bias & scale attributes can be used for fields of type f32/f64.
/// // this makes `a` range from 2 to 6 (as opposed to the default 0 to 1).
-/// #[bias = 2.0]
-/// #[scale = 4.0]
+/// #[bias(2.0)]
+/// #[scale(4.0)]
/// a: f64,
/// // we can even include a randomly-generated MyType inside this MyType!
/// // be careful when doing this or else you might try to generate an infinite struct!
@@ -176,46 +176,46 @@ mod tests {
#[derive(GenRandom, Debug)]
enum Test1 {
- #[prob = 0.2]
+ #[prob(0.2)]
A(f32),
- #[prob = 0.8]
+ #[prob(0.8)]
B(Option<f32>),
}
#[derive(GenRandom, Debug)]
#[allow(dead_code)]
enum Test2 {
- #[prob = 0.1]
+ #[prob(0.1)]
Variant1,
- #[prob = 0.7]
+ #[prob(0.7)]
Variant2 { x: f32, y: f64, z: Test1 },
- #[prob = 0.2]
+ #[prob(0.2)]
Variant3(f32, Box<Test2>),
}
#[derive(GenRandom, Debug)]
enum LinkedList {
- #[prob = 10]
+ #[prob(10)]
Empty,
- #[prob = 90]
+ #[prob(90)]
Cons(f32, Box<LinkedList>),
}
#[derive(GenRandom, Debug)]
enum BinaryTree {
- #[prob = 1]
+ #[prob(1)]
Empty,
- #[prob = 99]
+ #[prob(99)]
Node(f64, Box<BinaryTree>, Box<BinaryTree>)
}
#[derive(GenRandom, Debug)]
struct ScaleBias {
- #[bias = 1.0]
- #[scale = 10.0]
+ #[bias(1.0)]
+ #[scale(10.0)]
a: f32,
- #[bias = 2.0]
- #[scale = 0.0]
+ #[bias(2.0)]
+ #[scale(0.0)]
b: f32,
}
diff --git a/gen_random_proc_macro/src/lib.rs b/gen_random_proc_macro/src/lib.rs
index 5e28e16..9b7a771 100644
--- a/gen_random_proc_macro/src/lib.rs
+++ b/gen_random_proc_macro/src/lib.rs
@@ -19,7 +19,7 @@ pub fn gen_random_derive(input: TokenStream) -> TokenStream {
impl_gen_random(&ast)
}
-fn get_attribute_literal(attrs: &[syn::Attribute], name: &str) -> Option<proc_macro2::Literal> {
+fn get_attribute(attrs: &[syn::Attribute], name: &str) -> Option<proc_macro2::TokenStream> {
let attr = attrs.iter().find(|a| {
let path = &a.path;
if let Some(ident) = path.get_ident() {
@@ -30,25 +30,27 @@ fn get_attribute_literal(attrs: &[syn::Attribute], name: &str) -> Option<proc_ma
})?;
let tokens: TokenStream2 = attr.tokens.clone().into();
- let mut tokens: Vec<TokenTree2> = tokens.into_iter().collect();
- if tokens.len() != 2 {
- panic!("Expected {name} = <value>");
+ let tokens: Vec<TokenTree2> = tokens.into_iter().collect();
+ if tokens.len() != 1 {
+ panic!("Expected {name}(<value>)");
}
- use TokenTree2::{Punct, Literal};
- match &tokens[0] {
- Punct(equals) if equals.as_char() == '=' => {}
- _ => panic!("Expected = after {name} attribute"),
+ use TokenTree2::Group;
+ use proc_macro2::Delimiter;
+ let value = match &tokens[0] {
+ Group(g) if g.delimiter() == Delimiter::Parenthesis => {
+ g.stream()
+ },
+ _ => {
+ panic!("Expected {name}(<value>)");
+ },
};
- let Literal(literal) = tokens.remove(1) else {
- panic!("Bad value for {name} attribute.");
- };
- Some(literal)
+ Some(value)
}
fn parse_attribute_value<T: FromStr>(attrs: &[syn::Attribute], name: &str) -> Option<T> {
- let literal = get_attribute_literal(attrs, name)?;
- let Ok(value) = literal.to_string().parse() else {
+ let stream = get_attribute(attrs, name)?;
+ let Ok(value) = stream.to_string().parse() else {
panic!("Bad {} for {name} attribute", type_name::<T>())
};
Some(value)
@@ -63,11 +65,11 @@ fn generate_fields(fields: &syn::Fields) -> impl quote::ToTokens {
let ty = &field.ty;
field_values.extend(quote! { <#ty as GenRandom>::gen_random_max_depth(rng, _depth - 1) });
- if let Some(scale) = get_attribute_literal(&field.attrs, "scale") {
- field_values.extend(quote! { * #scale });
+ if let Some(scale) = get_attribute(&field.attrs, "scale") {
+ field_values.extend(quote! { * ( #scale ) });
}
- if let Some(bias) = get_attribute_literal(&field.attrs, "bias") {
- field_values.extend(quote! { + #bias });
+ if let Some(bias) = get_attribute(&field.attrs, "bias") {
+ field_values.extend(quote! { + ( #bias ) });
}
field_values.extend(quote! { , });
diff --git a/src/main.rs b/src/main.rs
index a9e9909..79b9d1f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,7 @@
/*
@TODO:
+- auto-select level set by sampling a bunch of points
+- Params instead of depth
- fullscreen key
- mathematical analysis
- options for:
@@ -65,16 +67,7 @@ impl View {
}
fn try_main() -> Result<(), String> {
-// use sdf::{Constant, R3ToR, R3ToR3, RToR};
-// let _test = Constant::gen_thread_random();
-// println!("{_test:?}");
-//
-// let funciton = R3ToR::compose(
-// R3ToR3::InfiniteMirrors(Constant::from(2.0)),
-// R3ToR::sphere_f32(0.2),
-// RToR::Identity,
-// );
- let my_sdf = sdf::Sdf::gen_thread_random_max_depth(6);
+ let my_sdf = sdf::Sdf::gen_thread_random_max_depth(7);
println!("{my_sdf:?}");
let mut window = win::Window::new("AutoSDF", 1280, 720, true)
diff --git a/src/sdf.rs b/src/sdf.rs
index 3c54d34..e6811ec 100644
--- a/src/sdf.rs
+++ b/src/sdf.rs
@@ -1,4 +1,3 @@
-#![allow(dead_code)] // @TODO @TEMPORARY
extern crate gen_random_proc_macro;
extern crate rand;
@@ -12,20 +11,47 @@ macro_rules! write_str {
}
/// these are constant across 3D space, not across time/user input/etc.
-#[derive(GenRandom, Debug)]
+#[derive(Debug, GenRandom)]
pub enum Constant {
- #[prob = 0.5]
+ #[prob(0.5)]
F32(f32),
- #[prob = 0]
- Time(f32, f32),
+ #[prob(0.5)]
+ Time(
+ #[scale(0.2)]
+ #[bias(-0.1)]
+ f32, f32),
}
+
+
impl From<f32> for Constant {
fn from(x: f32) -> Self {
Self::F32(x)
}
}
+impl std::ops::Add<f32> for Constant {
+ type Output = Self;
+ fn add(self, a: f32) -> Self::Output {
+ use Constant::*;
+ match self {
+ F32(x) => F32(x + a),
+ Time(s, b) => Time(s, b + a),
+ }
+ }
+}
+
+impl std::ops::Mul<f32> for Constant {
+ type Output = Self;
+ fn mul(self, m: f32) -> Self::Output {
+ use Constant::*;
+ match self {
+ F32(x) => F32(x * m),
+ Time(s, b) => Time(s * m, b * m),
+ }
+ }
+}
+
impl Display for Constant {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use Constant::*;
@@ -39,6 +65,20 @@ impl Display for Constant {
#[derive(GenRandom, Debug)]
pub struct Constant3(Constant, Constant, Constant);
+impl std::ops::Add<f32> for Constant3 {
+ type Output = Self;
+ fn add(self, a: f32) -> Self::Output {
+ Self(self.0 + a, self.1 + a, self.2 + a)
+ }
+}
+
+impl std::ops::Mul<f32> for Constant3 {
+ type Output = Self;
+ fn mul(self, m: f32) -> Self::Output {
+ Self(self.0 * m, self.1 * m, self.2 * m)
+ }
+}
+
impl From<(Constant, Constant, Constant)> for Constant3 {
fn from(x: (Constant, Constant, Constant)) -> Self {
Self(x.0, x.1, x.2)
@@ -54,41 +94,46 @@ impl Display for Constant3 {
#[derive(GenRandom, Debug)]
pub enum R3ToR3 {
- #[prob = 0]
+ #[prob(0)]
Identity,
- #[prob = 6]
+ #[prob(6)]
Compose(Box<R3ToR3>, Box<R3ToR3>),
- #[prob = 1]
+ #[prob(1)]
Translate(Constant3),
- #[prob = 2]
+ #[prob(2)]
Sin(Constant),
- #[prob = 2]
+ #[prob(2)]
InfiniteMirrors(Constant),
+ #[prob(2)]
+ #[scale(2 * std::f32::consts::PI)]
+ Rotate(Constant3),
+ #[prob(2)]
+ Arctan(Constant) // arctan(c x) / c
}
#[derive(GenRandom, Debug)]
pub enum RToR {
- #[prob = 0]
+ #[prob(0)]
Identity,
- #[prob = 2]
+ #[prob(2)]
Compose(Box<RToR>, Box<RToR>),
- #[prob = 2]
- Add(Constant),
+ #[prob(2)]
+ Subtract(Constant),
}
#[derive(GenRandom, Debug)]
pub enum R3ToR {
- #[prob = 1]
+ #[prob(1)]
Sphere(Constant),
- #[prob = 1]
+ #[prob(1)]
Cube(Constant),
- #[prob = 8]
+ #[prob(8)]
Compose(Box<R3ToR3>, Box<R3ToR>, Box<RToR>),
- #[prob = 4]
+ #[prob(4)]
Mix(Box<R3ToR>, Box<R3ToR>, Constant),
- #[prob = 2]
+ #[prob(2)]
SmoothMin(Box<R3ToR>, Box<R3ToR>),
- #[prob = 2]
+ #[prob(2)]
Min(Box<R3ToR>, Box<R3ToR>),
}
@@ -184,9 +229,9 @@ impl Function for RToR {
match self {
Identity => input,
- Add(x) => {
+ Subtract(x) => {
let output = var.next();
- write_str!(code, "float {output} = {input} + {x};\n");
+ write_str!(code, "float {output} = {input} - {x};\n");
output
}
Compose(a, b) => {
@@ -233,6 +278,31 @@ impl Function for R3ToR3 {
let a_output = a.to_glsl(input, code, var);
b.to_glsl(a_output, code, var)
}
+ Arctan(c) => {
+ let output = var.next();
+ // we need to scale arctan(cx) so it doesn't break the SDF
+ write_str!(code, "vec3 {output} = (1.0 / {c}) * atan({c} * {input});\n");
+ output
+ },
+ Rotate(by) => {
+ // by = euler angles
+ // see https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations
+ // for matrix
+ // this is the RzRyRx one
+ let c = var.next();
+ let s = var.next();
+ let m = var.next();
+ let output = var.next();
+ write_str!(code, "vec3 {c} = cos({by});\n");
+ write_str!(code, "vec3 {s} = sin({by});\n");
+ write_str!(code, "mat3 {m} = mat3(
+{c}.y*{c}.z, {s}.x*{s}.y*{c}.z - {c}.x*{s}.z, {c}.x*{s}.y*{c}.z + {s}.x*{s}.z,
+{c}.y*{s}.z, {s}.x*{s}.y*{s}.z + {c}.x*{c}.z, {c}.x*{s}.y*{s}.z - {s}.x*{c}.z,
+-{s}.y, {s}.x*{c}.y, {c}.x*{c}.y
+);\n");
+ write_str!(code, "vec3 {output} = {m} * {input};\n");
+ output
+ }
}
}
}