-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |