Skip to content

Commit

Permalink
add hierarchy_fractal example
Browse files Browse the repository at this point in the history
  • Loading branch information
CptPotato committed Mar 10, 2022
1 parent 1a85fb5 commit a643441
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ path = "examples/2d/texture_atlas.rs"
name = "3d_scene"
path = "examples/3d/3d_scene.rs"

[[example]]
name = "hierarchy_fractal"
path = "examples/3d/hierarchy_fractal.rs"

[[example]]
name = "lighting"
path = "examples/3d/lighting.rs"
Expand Down
216 changes: 216 additions & 0 deletions examples/3d/hierarchy_fractal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use bevy::prelude::*;

// tree width (per node) and depth
// results in 1 + WIDTH^(DEPTH - 1) total
const WIDTH: usize = 3;
const DEPTH: usize = 6;

// line size
const BASE_LENGTH: f32 = 18.0;
const BASE_WIDTH: f32 = 0.15;

// mutation parameters for branching (uses 0..WIDTH)
const OFFSETS: [f32; 5] = [0.19, 0.42, 0.68, 0.33, 0.73];

// color palette parameters
// see: https://iquilezles.org/www/articles/palettes/palettes.htm
const PALETTE: [(f32, f32, f32); 4] = [
(0.5, 0.5, 0.5),
(0.5, 0.5, 0.5),
(1.0, 1.0, 1.0),
(0.00, 0.10, 0.20),
];

#[derive(Component)]
struct Rotate {
pub speed: f32,
pub offset: f32,
pub axis: Vec3,
}

fn rotate(time: Res<Time>, mut query: Query<(&Rotate, &mut Transform)>) {
for (rotate, mut transform) in query.iter_mut() {
let angle = rotate.offset + rotate.speed * (time.seconds_since_startup() as f32);
transform.rotation = Quat::from_axis_angle(rotate.axis, angle);
}
}

fn center_camera(
query_fractal: Query<&GlobalTransform, With<Rotate>>,
mut query_cameras: Query<&mut Transform, With<Camera>>,
) {
// average line positions
let mut center = Vec3::ZERO;
let mut count = 0;
for transform in query_fractal.iter() {
center += transform.translation;
count += 1;
}
center /= count as f32;

// set camera positions
for mut transform in query_cameras.iter_mut() {
transform.translation.x = center.x;
transform.translation.y = center.y;
}
}

// state for building the tree
struct NodeState {
axis: Vec3, // rotation axis
param_a: f32, // increases (used for color)
param_b: f32, // decreases 1/x^2 (used for line size)
param_c: f32, // decreases similar to param_b (used for rotation speed)
}

impl NodeState {
// `p` is used to change properties with different intensities for each branch
pub fn mutate(&self, p: f32) -> Self {
Self {
axis: Quat::from_rotation_x(self.param_a)
* Quat::from_rotation_y(self.param_b)
* self.axis,
param_a: self.param_a + p * 0.5 * self.param_b,
param_b: self.param_b * 0.4 + p * 0.6,
param_c: self.param_b * p * 0.3,
}
}
}

// https://iquilezles.org/www/articles/palettes/palettes.htm
fn palette(t: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
a + b * f32::cos(6.28318 * (c * t + d))
}

fn spawn_recursive(
depth: usize,
state: NodeState,
parent_offset: f32,
commands: &mut Commands,
meshes: &mut Assets<Mesh>,
materials: &mut Assets<StandardMaterial>,
) -> Option<Entity> {
if depth == 0 {
return None;
}

let line_length = state.param_b * BASE_LENGTH;
let line_width = state.param_b * BASE_WIDTH;

let mesh = meshes.add(Mesh::from(shape::Box {
min_x: 0.0,
max_x: line_length,
min_y: -line_width,
max_y: line_width,
min_z: -line_width,
max_z: line_width,
}));

let material = materials.add(StandardMaterial {
emissive: Color::rgb(
palette(
state.param_a,
PALETTE[0].0,
PALETTE[1].0,
PALETTE[2].0,
PALETTE[3].0,
) * 1.5,
palette(
state.param_a,
PALETTE[0].1,
PALETTE[1].1,
PALETTE[2].1,
PALETTE[3].1,
) * 1.5,
palette(
state.param_a,
PALETTE[0].2,
PALETTE[1].2,
PALETTE[2].2,
PALETTE[3].2,
) * 1.5,
),
..default()
});

let speed = state.param_c;
let offset = state.param_a;
let axis = state.axis;

let id = commands
.spawn_bundle(PbrBundle {
mesh,
material,
transform: Transform {
translation: Vec3::new(parent_offset, 0.0, 0.0),
rotation: Quat::from_axis_angle(axis, offset),
..default()
},
..default()
})
.insert(Rotate {
speed,
offset,
axis,
})
.id();

let depth = depth - 1;
let children: Vec<Entity> = (0..WIDTH)
.filter_map(|w| {
spawn_recursive(
depth,
state.mutate(*OFFSETS.get(w).expect("not enough offsets for branch width")),
line_length,
commands,
meshes,
materials,
)
})
.collect();

commands.entity(id).push_children(&children);

Some(id)
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let mut camera = PerspectiveCameraBundle::new_3d();
camera.transform.translation.z = 100.0;
commands.spawn_bundle(camera);

let state = NodeState {
axis: Vec3::Z,
param_a: 0.0,
param_b: 1.0,
param_c: 0.04,
};

spawn_recursive(
DEPTH,
state,
0.0,
&mut commands,
&mut (*meshes),
&mut (*materials),
);
}

fn main() {
App::new()
.insert_resource(ClearColor(Color::BLACK))
.insert_resource(WindowDescriptor {
width: 1400.0,
height: 900.0,
..default()
})
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(rotate.label("rotate"))
.add_system(center_camera.after("rotate"))
.run()
}

0 comments on commit a643441

Please sign in to comment.