From 14530cc8455c61eca8895b07dba503556c1200d9 Mon Sep 17 00:00:00 2001 From: mebyz Date: Thu, 25 May 2023 19:16:37 +0400 Subject: [PATCH 1/9] a forest of proc mesh/mat based on proc trees --- guest/rust/Cargo.lock | 23 +- .../rust/examples/advanced/forest/Cargo.toml | 25 + .../examples/advanced/forest/ambient.toml | 7 + .../examples/advanced/forest/src/client.rs | 562 ++++++++++++++++++ 4 files changed, 612 insertions(+), 5 deletions(-) create mode 100644 guest/rust/examples/advanced/forest/Cargo.toml create mode 100644 guest/rust/examples/advanced/forest/ambient.toml create mode 100644 guest/rust/examples/advanced/forest/src/client.rs diff --git a/guest/rust/Cargo.lock b/guest/rust/Cargo.lock index a9acd36f0f..c81876da61 100644 --- a/guest/rust/Cargo.lock +++ b/guest/rust/Cargo.lock @@ -587,6 +587,19 @@ dependencies = [ "ambient_api", ] +[[package]] +name = "forest" +version = "0.3.0-dev" +dependencies = [ + "ambient_api", + "noise", + "palette 0.7.1", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_pcg", + "serde", +] + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -1483,22 +1496,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] diff --git a/guest/rust/examples/advanced/forest/Cargo.toml b/guest/rust/examples/advanced/forest/Cargo.toml new file mode 100644 index 0000000000..0af9349bf4 --- /dev/null +++ b/guest/rust/examples/advanced/forest/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "forest" + +edition = "2021" +publish = false +rust-version = { workspace = true } +version = { workspace = true } + +[dependencies] +ambient_api = { workspace = true } +rand_pcg = "0.3.1" +noise = { version = "0.8.2", features = ["images"] } +palette = "0.7.1" +rand = "0.8.5" +rand_chacha = "0.3.1" +serde = "1.0.163" + +[[bin]] +name = "forest_client" +path = "src/client.rs" +required-features = ["client"] + +[features] +client = ["ambient_api/client"] +server = ["ambient_api/server"] diff --git a/guest/rust/examples/advanced/forest/ambient.toml b/guest/rust/examples/advanced/forest/ambient.toml new file mode 100644 index 0000000000..a091fd1229 --- /dev/null +++ b/guest/rust/examples/advanced/forest/ambient.toml @@ -0,0 +1,7 @@ +[project] +id = "forest" +name = "A forest" +version = "0.0.1" + +[components] +rotating_sun = { type = "Bool", name = "Rotating sun", description = "Whether or not rotate the sun automatically" } diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs new file mode 100644 index 0000000000..b4b4ec940f --- /dev/null +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -0,0 +1,562 @@ +use ambient_api::{ + client::{material, mesh, sampler, texture}, + components::core::{ + camera::aspect_ratio_from_window, + procedurals::{procedural_material, procedural_mesh}, + primitives::quad, + }, + concepts::{make_perspective_infinite_reverse_camera, make_transformable}, + prelude::*, + mesh::Vertex, +}; + +use components::rotating_sun; +use noise::{utils::*, Fbm, Perlin}; +use palette::IntoColor; +use glam::*; +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; + +const TAU: f32 = std::f32::consts::TAU; +const RESOLUTION_X: u32 = 32; +const RESOLUTION_Y: u32 = 8; +const TEXTURE_RESOLUTION_X: u32 = 4 * RESOLUTION_X; +const TEXTURE_RESOLUTION_Y: u32 = 4 * RESOLUTION_Y; +const SIZE_X: f32 = RESOLUTION_X as f32 / RESOLUTION_Y as f32; +const SIZE_Y: f32 = 1.0; +const WAVE_AMPLITUDE: f32 = 0.25; +const WAVE_FREQUENCY: f32 = 0.5 * TAU; + +#[derive(Clone)] +pub struct TreeMesh { + pub seed: i32, + pub trunk_radius: f32, + pub trunk_height: f32, + pub trunk_segments: u32, + pub foliage_radius: f32, + pub foliage_density: u32, + pub foliage_segments: u32, +} + +impl Default for TreeMesh { + fn default() -> Self { + Self { + seed: 123 as i32, + trunk_radius: 3.0, + trunk_height: 15.0, + trunk_segments: 8, + foliage_radius: 2.0, + foliage_density: 5, + foliage_segments: 5, + } + } +} +pub struct MeshDescriptor { + vertices: Vec, + indices: Vec, +} + +pub fn create_tree(t: TreeMesh , seed: i32, trunk_radius: f32, trunk_height: f32, trunk_segments: u32) -> MeshDescriptor { + let mut tree = t; + + tree.trunk_radius = trunk_radius; + tree.trunk_height = trunk_height; + tree.trunk_segments = trunk_segments; + tree.seed = seed; + + // Create the trunk + let (mut vertices1, top_vertices1, mut normals1, mut uvs1) = build_trunk(&tree); + + let sectors = 12; + let trunk_segments = tree.trunk_segments; + let mut indices = Vec::new(); + + // Connect trunk segments + for i in 0..(trunk_segments) { + for j in 0..sectors { + let k1 = (i * (sectors as u32 + 1) + j as u32) as u32; + let k2 = ((i + 1) * (sectors as u32 + 1) + j as u32) as u32; + + indices.push(k1); + indices.push(k1 + 1); + indices.push(k2); + + indices.push(k1 + 1); + indices.push(k2 + 1); + indices.push(k2); + } + } + + // Generate foliage + let foliage_count = tree.foliage_density + tree.foliage_segments; + let foliage_radius_variance = 0.05; + let foliage_position_variance = vec3(5.0, 5.0, 3.0); + + for i in 0..foliage_count { + let foliage_radius = tree.foliage_radius + * (1.0 - gen_rn(tree.seed + i as i32, 0.0, 1.0) * foliage_radius_variance); + let foliage_position = top_vertices1 + [gen_rn(tree.seed, 0.0, top_vertices1.len() as f32) as usize] + + vec3( + gen_rn(tree.seed + i as i32, 0.0, 1.0) * foliage_position_variance.x, + gen_rn(tree.seed + i as i32 + 1, 0.0, 1.0) * foliage_position_variance.y, + gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) * foliage_position_variance.z, + ); + + let segments = tree.foliage_segments; + let density = tree.foliage_density; + let sector_step = 2. * std::f32::consts::PI / density as f32; + + let mut sphere_vertices = Vec::new(); + let mut sphere_normals = Vec::new(); + let mut sphere_uvs = Vec::new(); + + for i in 0..=segments { + let theta = (i as f32 / segments as f32) * std::f32::consts::PI; + let height = foliage_position.z + (foliage_radius * theta.cos()); + let segment_radius = foliage_radius * theta.sin(); + + for j in 0..=density { + let phi = j as f32 * sector_step; + let x = foliage_position.x + segment_radius * phi.cos(); + let y = foliage_position.y + segment_radius * phi.sin(); + let z = height; + + sphere_vertices.push(vec3(x, y, z)); + sphere_normals.push( + vec3( + x - foliage_position.x, + y - foliage_position.y, + z - foliage_position.z, + ) + .normalize(), + ); + sphere_uvs.push(vec2(j as f32 / density as f32, i as f32 / segments as f32)); + } + } + + let sphere_indices = generate_sphere_indices(segments as usize, density as usize); + + vertices1.extend(sphere_vertices.clone()); + normals1.extend(sphere_normals); + uvs1.extend(sphere_uvs); + + let offset = vertices1.len() - sphere_vertices.len(); + indices.extend(sphere_indices.iter().map(|i| *i + offset as u32)); + } + + // Function to generate indices for a sphere based on segments and density + fn generate_sphere_indices(segments: usize, density: usize) -> Vec { + let mut indices = Vec::with_capacity(segments * density * 6); + + for i in 0..segments { + for j in 0..density { + let index1 = i * (density + 1) + j; + let index2 = index1 + 1; + let index3 = (i + 1) * (density + 1) + j; + let index4 = index3 + 1; + + indices.push(index1 as u32); + indices.push(index2 as u32); + indices.push(index3 as u32); + + indices.push(index2 as u32); + indices.push(index4 as u32); + indices.push(index3 as u32); + } + } + indices + } + + let mut vec_of_vertex : Vec = Vec::with_capacity(vertices1.len()); + + for i in 0..vertices1.len() { + let px = vertices1[i].x; + let py = vertices1[i].y; + let pz = vertices1[i].z; + let u = uvs1[i].x; + let v = uvs1[i].y; + let nx = normals1[i].x; + let ny = normals1[i].y; + let nz = normals1[i].z; + + let v = mesh::Vertex { + position: vec3(px, py, pz) + vec3(-0.5 * SIZE_X, -0.5 * SIZE_Y, 0.0), + normal: vec3(nx, ny, nz), + tangent: vec3(1.0, 0.0, 0.0), + texcoord0: vec2(u, v), + }; + vec_of_vertex.push(v); + } + + let vcs = vec_of_vertex.clone(); + let ids = indices.clone(); + + MeshDescriptor { + vertices: vec_of_vertex, + indices: indices.clone(), + } +} + +fn build_trunk(tree: &TreeMesh) -> (Vec, Vec, Vec, Vec) { + let sectors = 12; + let sector_step = 2. * std::f32::consts::PI / sectors as f32; + + let mut vertices = Vec::new(); + let mut top_vertices = Vec::new(); + let mut normals = Vec::new(); + let mut uvs = Vec::new(); + + let mut trunk_direction = vec3(0.0, 0.0, 1.0); + let direction_variance = 0.08; + + let radius_variance = 0.02; + + for i in 0..=tree.trunk_segments { + let variance = gen_rn(tree.seed + i as i32, 0.0, 1.0) * radius_variance; + let z = tree.trunk_height * (i as f32 / tree.trunk_segments as f32); + let s = tree.trunk_radius; + let radius = s * (1.0 - i as f32 / tree.trunk_segments as f32) * (1.0 - variance); + + let top_segment_radius = tree.trunk_radius * 0.1; + let radius = if i == tree.trunk_segments && radius < top_segment_radius { + top_segment_radius + } else { + radius + }; + + let random_direction = vec3( + gen_rn(tree.seed + i as i32 + 1, 0.0, 1.0) - 0.5, + gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) - 0.5, + gen_rn(tree.seed + i as i32 + 3, 0.0, 1.0) - 0.5, + ) + .normalize() + * direction_variance; + trunk_direction = (trunk_direction + random_direction).normalize(); + + let top_position = trunk_direction * z; + + let gravity_factor = (1.0 - (i as f32 / tree.trunk_segments as f32)).powf(2.0); + let gravity_offset = trunk_direction * gravity_factor * 2.0 * i as f32; + + for j in 0..=sectors { + let sector_angle = j as f32 * sector_step; + let x = radius * sector_angle.cos(); + let y = radius * sector_angle.sin(); + + vertices.push(top_position + vec3(x, y, 0.0) - gravity_offset); + normals.push(vec3(x, y, 0.0).normalize()); + uvs.push(vec2( + j as f32 / sectors as f32, + i as f32 / tree.trunk_segments as f32, + )); + } + + if i == tree.trunk_segments { + let top_vertex_start = vertices.len() - sectors - 1; + let top_vertex_end = vertices.len(); + top_vertices.extend(vertices[top_vertex_start..top_vertex_end].iter().cloned()); + + // Add faces to connect the last ring of vertices + for j in 0..sectors { + let v1 = top_vertex_start + j; + let v2 = top_vertex_start + j + 1; + let v3 = top_vertex_end - 1; + let v4 = top_vertex_start; + + // First triangle + vertices.push(vertices[v1] - gravity_offset); + vertices.push(vertices[v2] - gravity_offset); + vertices.push(vertices[v3] - gravity_offset); + + normals.push(normals[v1]); + normals.push(normals[v2]); + normals.push(normals[v3]); + + uvs.push(uvs[v1]); + uvs.push(uvs[v2]); + uvs.push(uvs[v3]); + + // Second triangle + vertices.push(vertices[v1] - gravity_offset); + vertices.push(vertices[v3] - gravity_offset); + vertices.push(vertices[v4] - gravity_offset); + + normals.push(normals[v1]); + normals.push(normals[v3]); + normals.push(normals[v4]); + + uvs.push(uvs[v1]); + uvs.push(uvs[v3]); + uvs.push(uvs[v4]); + } + } + } + + (vertices, top_vertices, normals, uvs) +} + +pub fn gen_rn(seed: i32, min: f32, max: f32) -> f32 { + let mut rng = ChaCha8Rng::seed_from_u64(seed as u64); + rng.gen_range(min..max) +} + +fn make_camera() { + Entity::new() + .with_merge(make_perspective_infinite_reverse_camera()) + .with(aspect_ratio_from_window(), EntityId::resources()) + .with_default(main_scene()) + .with(translation(), vec3(10.0, 10.0, 4.0) * 2.0) + .with(lookat_target(), vec3(0.0, 3.0, 0.0)) + .spawn(); +} + +#[element_component] +fn App(_hooks: &mut Hooks, sun_id: EntityId) -> Element { + FocusRoot::el([FlowColumn::el([FlowRow::el([Button::new( + "Toggle sun rotation", + move |_| { + entity::mutate_component(sun_id, rotating_sun(), |rotating_sun| { + *rotating_sun = !*rotating_sun; + }); + }, + ) + .el()])]) + .with_padding_even(10.0)]) +} + +fn make_lighting() { + let sun_id = Entity::new() + .with_merge(make_transformable()) + .with_default(sun()) + .with( + rotation(), + Quat::from_rotation_y(-45_f32.to_radians()) + * Quat::from_rotation_z(-45_f32.to_radians()), + ) + .with(light_diffuse(), Vec3::ONE * 4.0) + .with_default(main_scene()) + .with(rotating_sun(), false) + .spawn(); + App::el(sun_id).spawn_interactive(); + query((rotation(), (rotating_sun()))) + .requires(sun()) + .each_frame(move |suns| { + for (sun_id, (sun_rotation, rotating_sun)) in suns { + if !rotating_sun { + continue; + } + entity::set_component( + sun_id, + rotation(), + Quat::from_rotation_z(frametime()) * sun_rotation, + ); + } + }); +} + +fn make_ground() { + Entity::new() + .with_merge(make_transformable()) + .with_default(quad()) + .with(color(), vec4(0.25, 1.0, 0.25, 1.0)) + .with(translation(), vec3(0.0, 0.0, -0.5)) + .with(scale(), 32.0 * Vec3::ONE) + .spawn(); +} + +fn make_texture(mut pixel_fn: PixelFn) -> ProceduralTextureHandle +where + PixelFn: FnMut(f32, f32) -> [u8; 4], +{ + let mut pixels = vec![0_u8; (4 * TEXTURE_RESOLUTION_X * TEXTURE_RESOLUTION_Y) as usize]; + for y in 0..TEXTURE_RESOLUTION_Y { + for x in 0..TEXTURE_RESOLUTION_X { + let dst = (4 * (x + y * TEXTURE_RESOLUTION_X)) as usize; + let dst = &mut pixels[dst..(dst + 4)]; + let px = (x as f32 + 0.5) / (TEXTURE_RESOLUTION_X as f32); + let py = (y as f32 + 0.5) / (TEXTURE_RESOLUTION_Y as f32); + dst.copy_from_slice(&pixel_fn(px, py)); + } + } + texture::create_2d(&texture::Descriptor2D { + width: TEXTURE_RESOLUTION_X, + height: TEXTURE_RESOLUTION_Y, + format: texture::Format::Rgba8Unorm, + data: &pixels, + }) +} + +fn default_base_color(_: f32, _: f32) -> [u8; 4] { + [255, 255, 255, 255] +} + +fn default_normal(_: f32, _: f32) -> [u8; 4] { + [128, 128, 255, 0] +} + +fn default_metallic_roughness(_: f32, _: f32) -> [u8; 4] { + [255, 255, 0, 0] +} + +fn default_nearest_sampler() -> ProceduralSamplerHandle { + sampler::create(&sampler::Descriptor { + address_mode_u: sampler::AddressMode::ClampToEdge, + address_mode_v: sampler::AddressMode::ClampToEdge, + address_mode_w: sampler::AddressMode::ClampToEdge, + mag_filter: sampler::FilterMode::Nearest, + min_filter: sampler::FilterMode::Nearest, + mipmap_filter: sampler::FilterMode::Nearest, + }) +} + +fn default_linear_sampler() -> ProceduralSamplerHandle { + sampler::create(&sampler::Descriptor { + address_mode_u: sampler::AddressMode::ClampToEdge, + address_mode_v: sampler::AddressMode::ClampToEdge, + address_mode_w: sampler::AddressMode::ClampToEdge, + mag_filter: sampler::FilterMode::Linear, + min_filter: sampler::FilterMode::Linear, + mipmap_filter: sampler::FilterMode::Linear, + }) +} + +fn make_procedural( + world_translation: Vec3, + base_color_fn: BaseColorFn, + normal_fn: NormalFn, + metallic_roughness_fn: MetallicRoughnessFn, + sampler_fn: SamplerFn, + transparent: bool, +) where + BaseColorFn: FnMut(f32, f32) -> [u8; 4], + NormalFn: FnMut(f32, f32) -> [u8; 4], + MetallicRoughnessFn: FnMut(f32, f32) -> [u8; 4], + SamplerFn: FnOnce() -> ProceduralSamplerHandle, +{ + let mut vertices = vec![]; + let mut indices = vec![]; + for y in 0..=RESOLUTION_Y { + for x in 0..=RESOLUTION_X { + let px = SIZE_X * (x as f32) / (RESOLUTION_X as f32); + let py = SIZE_Y * (y as f32) / (RESOLUTION_Y as f32); + let pz = WAVE_AMPLITUDE * f32::sin(WAVE_FREQUENCY * px); + let u = (x as f32) / (RESOLUTION_X as f32); + let v = (y as f32) / (RESOLUTION_Y as f32); + vertices.push(mesh::Vertex { + position: vec3(px, py, pz) + vec3(-0.5 * SIZE_X, -0.5 * SIZE_Y, 0.0), + normal: vec3(0.0, 0.0, 1.0), + tangent: vec3(1.0, 0.0, 0.0), + texcoord0: vec2(u, v), + }); + } + } + for y in 0..RESOLUTION_Y { + for x in 0..RESOLUTION_X { + let i0 = x + y * (RESOLUTION_X + 1); + let i1 = (x + 1) + y * (RESOLUTION_X + 1); + let i2 = x + (y + 1) * (RESOLUTION_X + 1); + let i3 = (x + 1) + (y + 1) * (RESOLUTION_X + 1); + indices.extend([i0, i1, i2]); + indices.extend([i1, i3, i2]); + } + } + for triangle in indices.chunks_exact(3) { + let [i0, i1, i2]: [_; 3] = triangle.try_into().unwrap(); + let p0 = vertices[i0 as usize].position; + let p1 = vertices[i1 as usize].position; + let p2 = vertices[i2 as usize].position; + let n01 = (p1 - p0).normalize(); + let n02 = (p2 - p0).normalize(); + let n = n01.cross(n02).normalize(); + vertices[i0 as usize].normal += n; + vertices[i1 as usize].normal += n; + vertices[i2 as usize].normal += n; + } + for vertex in &mut vertices { + vertex.normal = vertex.normal.normalize(); + } + + let seed = 12345; + let num_trees = 15; + + let base_color_map = make_texture(base_color_fn); + let normal_map = make_texture(normal_fn); + let metallic_roughness_map = make_texture(metallic_roughness_fn); + let sampler = sampler_fn(); + let material = material::create(&material::Descriptor { + base_color_map, + normal_map, + metallic_roughness_map, + sampler, + transparent, + }); + + // lets plant some trees :) + for i in 0..num_trees { + let trunk_radius = gen_rn(seed + i, 2.0, 3.0); + let trunk_height = gen_rn(seed + i, 15.0, 20.0); + let trunk_segments = gen_rn(seed + i, 6.0, 12.0) as u32; + + let tree = create_tree(TreeMesh::default(), seed, trunk_radius, trunk_height, trunk_segments); + + let td = mesh::Descriptor { + vertices: &tree.vertices, + indices: &tree.indices, + }; + + let mesh = mesh::create(&td); + + Entity::new() + .with_merge(make_transformable()) + .with(procedural_mesh(), mesh) + .with(procedural_material(), material) + .with_default(cast_shadows()) + .with(scale(), Vec3::ONE * gen_rn(i, 0.2, 0.4)) + .with( + translation(), + vec3( + gen_rn(seed + i, 0.0, 15.0), + gen_rn(seed + seed + i, 0.0, 15.0), + 0.0, + ), + ) + .spawn(); +} + +} + +fn make_procedurals() { + const X: f32 = 2.125; + const Y: f32 = 1.25; + + let rng = rand_pcg::Pcg64::seed_from_u64(0); + let dist_zero_to_255 = rand::distributions::Uniform::new_inclusive(0_u8, 255_u8); + let dist_minus_one_to_one = rand::distributions::Uniform::new_inclusive(-1.0_f32, 1.0_f32); + + // Interpolated hue. + make_procedural( + vec3(-X, Y, 0.0), + |x, _| { + let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); + let rgb: palette::LinSrgb = hsl.into_color(); + let r = (255.0 * rgb.red) as u8; + let g = (255.0 * rgb.green) as u8; + let b = (255.0 * rgb.blue) as u8; + let a = 255; + [r, g, b, a] + }, + default_normal, + default_metallic_roughness, + default_nearest_sampler, + false, + ); +} + +#[main] +pub async fn main() { + make_camera(); + make_lighting(); + make_ground(); + make_procedurals(); +} From 2bce3fbf496b0cead8edc015b6c891a9328c5aa8 Mon Sep 17 00:00:00 2001 From: Philpax Date: Thu, 25 May 2023 17:43:44 +0200 Subject: [PATCH 2/9] cargo fmt --- .../examples/advanced/forest/src/client.rs | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index b4b4ec940f..668e1248c1 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -2,18 +2,18 @@ use ambient_api::{ client::{material, mesh, sampler, texture}, components::core::{ camera::aspect_ratio_from_window, - procedurals::{procedural_material, procedural_mesh}, primitives::quad, + procedurals::{procedural_material, procedural_mesh}, }, concepts::{make_perspective_infinite_reverse_camera, make_transformable}, - prelude::*, mesh::Vertex, + prelude::*, }; use components::rotating_sun; +use glam::*; use noise::{utils::*, Fbm, Perlin}; use palette::IntoColor; -use glam::*; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; @@ -56,7 +56,13 @@ pub struct MeshDescriptor { indices: Vec, } -pub fn create_tree(t: TreeMesh , seed: i32, trunk_radius: f32, trunk_height: f32, trunk_segments: u32) -> MeshDescriptor { +pub fn create_tree( + t: TreeMesh, + seed: i32, + trunk_radius: f32, + trunk_height: f32, + trunk_segments: u32, +) -> MeshDescriptor { let mut tree = t; tree.trunk_radius = trunk_radius; @@ -168,7 +174,7 @@ pub fn create_tree(t: TreeMesh , seed: i32, trunk_radius: f32, trunk_height: f32 indices } - let mut vec_of_vertex : Vec = Vec::with_capacity(vertices1.len()); + let mut vec_of_vertex: Vec = Vec::with_capacity(vertices1.len()); for i in 0..vertices1.len() { let px = vertices1[i].x; @@ -498,7 +504,13 @@ fn make_procedural( let trunk_height = gen_rn(seed + i, 15.0, 20.0); let trunk_segments = gen_rn(seed + i, 6.0, 12.0) as u32; - let tree = create_tree(TreeMesh::default(), seed, trunk_radius, trunk_height, trunk_segments); + let tree = create_tree( + TreeMesh::default(), + seed, + trunk_radius, + trunk_height, + trunk_segments, + ); let td = mesh::Descriptor { vertices: &tree.vertices, @@ -508,22 +520,21 @@ fn make_procedural( let mesh = mesh::create(&td); Entity::new() - .with_merge(make_transformable()) - .with(procedural_mesh(), mesh) - .with(procedural_material(), material) - .with_default(cast_shadows()) - .with(scale(), Vec3::ONE * gen_rn(i, 0.2, 0.4)) - .with( - translation(), - vec3( - gen_rn(seed + i, 0.0, 15.0), - gen_rn(seed + seed + i, 0.0, 15.0), - 0.0, - ), - ) - .spawn(); -} - + .with_merge(make_transformable()) + .with(procedural_mesh(), mesh) + .with(procedural_material(), material) + .with_default(cast_shadows()) + .with(scale(), Vec3::ONE * gen_rn(i, 0.2, 0.4)) + .with( + translation(), + vec3( + gen_rn(seed + i, 0.0, 15.0), + gen_rn(seed + seed + i, 0.0, 15.0), + 0.0, + ), + ) + .spawn(); + } } fn make_procedurals() { From 9686ebaa6b24ba3f201f55af49356b779968b589 Mon Sep 17 00:00:00 2001 From: Philpax Date: Thu, 25 May 2023 18:08:27 +0200 Subject: [PATCH 3/9] refactor(forest): clean up --- .../examples/advanced/forest/src/client.rs | 60 +++---------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index 668e1248c1..4a70e3da3d 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -12,7 +12,6 @@ use ambient_api::{ use components::rotating_sun; use glam::*; -use noise::{utils::*, Fbm, Perlin}; use palette::IntoColor; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; @@ -56,20 +55,7 @@ pub struct MeshDescriptor { indices: Vec, } -pub fn create_tree( - t: TreeMesh, - seed: i32, - trunk_radius: f32, - trunk_height: f32, - trunk_segments: u32, -) -> MeshDescriptor { - let mut tree = t; - - tree.trunk_radius = trunk_radius; - tree.trunk_height = trunk_height; - tree.trunk_segments = trunk_segments; - tree.seed = seed; - +pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { // Create the trunk let (mut vertices1, top_vertices1, mut normals1, mut uvs1) = build_trunk(&tree); @@ -174,7 +160,7 @@ pub fn create_tree( indices } - let mut vec_of_vertex: Vec = Vec::with_capacity(vertices1.len()); + let mut vertices: Vec = Vec::with_capacity(vertices1.len()); for i in 0..vertices1.len() { let px = vertices1[i].x; @@ -192,16 +178,10 @@ pub fn create_tree( tangent: vec3(1.0, 0.0, 0.0), texcoord0: vec2(u, v), }; - vec_of_vertex.push(v); + vertices.push(v); } - let vcs = vec_of_vertex.clone(); - let ids = indices.clone(); - - MeshDescriptor { - vertices: vec_of_vertex, - indices: indices.clone(), - } + MeshDescriptor { vertices, indices } } fn build_trunk(tree: &TreeMesh) -> (Vec, Vec, Vec, Vec) { @@ -393,10 +373,6 @@ where }) } -fn default_base_color(_: f32, _: f32) -> [u8; 4] { - [255, 255, 255, 255] -} - fn default_normal(_: f32, _: f32) -> [u8; 4] { [128, 128, 255, 0] } @@ -416,19 +392,7 @@ fn default_nearest_sampler() -> ProceduralSamplerHandle { }) } -fn default_linear_sampler() -> ProceduralSamplerHandle { - sampler::create(&sampler::Descriptor { - address_mode_u: sampler::AddressMode::ClampToEdge, - address_mode_v: sampler::AddressMode::ClampToEdge, - address_mode_w: sampler::AddressMode::ClampToEdge, - mag_filter: sampler::FilterMode::Linear, - min_filter: sampler::FilterMode::Linear, - mipmap_filter: sampler::FilterMode::Linear, - }) -} - fn make_procedural( - world_translation: Vec3, base_color_fn: BaseColorFn, normal_fn: NormalFn, metallic_roughness_fn: MetallicRoughnessFn, @@ -504,13 +468,15 @@ fn make_procedural( let trunk_height = gen_rn(seed + i, 15.0, 20.0); let trunk_segments = gen_rn(seed + i, 6.0, 12.0) as u32; - let tree = create_tree( - TreeMesh::default(), + let tree = TreeMesh { seed, trunk_radius, trunk_height, trunk_segments, - ); + ..Default::default() + }; + + let tree = create_tree(tree); let td = mesh::Descriptor { vertices: &tree.vertices, @@ -538,16 +504,8 @@ fn make_procedural( } fn make_procedurals() { - const X: f32 = 2.125; - const Y: f32 = 1.25; - - let rng = rand_pcg::Pcg64::seed_from_u64(0); - let dist_zero_to_255 = rand::distributions::Uniform::new_inclusive(0_u8, 255_u8); - let dist_minus_one_to_one = rand::distributions::Uniform::new_inclusive(-1.0_f32, 1.0_f32); - // Interpolated hue. make_procedural( - vec3(-X, Y, 0.0), |x, _| { let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); let rgb: palette::LinSrgb = hsl.into_color(); From 13622385ed1cc702dbdbd2e229001f2417e6e808 Mon Sep 17 00:00:00 2001 From: Philpax Date: Thu, 25 May 2023 18:18:23 +0200 Subject: [PATCH 4/9] refactor(forest): more simplification --- .../examples/advanced/forest/src/client.rs | 121 +++--------------- 1 file changed, 21 insertions(+), 100 deletions(-) diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index 4a70e3da3d..82fbcd94a6 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -11,20 +11,16 @@ use ambient_api::{ }; use components::rotating_sun; -use glam::*; use palette::IntoColor; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; -const TAU: f32 = std::f32::consts::TAU; const RESOLUTION_X: u32 = 32; const RESOLUTION_Y: u32 = 8; const TEXTURE_RESOLUTION_X: u32 = 4 * RESOLUTION_X; const TEXTURE_RESOLUTION_Y: u32 = 4 * RESOLUTION_Y; const SIZE_X: f32 = RESOLUTION_X as f32 / RESOLUTION_Y as f32; const SIZE_Y: f32 = 1.0; -const WAVE_AMPLITUDE: f32 = 0.25; -const WAVE_FREQUENCY: f32 = 0.5 * TAU; #[derive(Clone)] pub struct TreeMesh { @@ -40,7 +36,7 @@ pub struct TreeMesh { impl Default for TreeMesh { fn default() -> Self { Self { - seed: 123 as i32, + seed: 123, trunk_radius: 3.0, trunk_height: 15.0, trunk_segments: 8, @@ -66,8 +62,8 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { // Connect trunk segments for i in 0..(trunk_segments) { for j in 0..sectors { - let k1 = (i * (sectors as u32 + 1) + j as u32) as u32; - let k2 = ((i + 1) * (sectors as u32 + 1) + j as u32) as u32; + let k1 = i * (sectors + 1) + j; + let k2 = (i + 1) * (sectors + 1) + j; indices.push(k1); indices.push(k1 + 1); @@ -373,93 +369,37 @@ where }) } -fn default_normal(_: f32, _: f32) -> [u8; 4] { - [128, 128, 255, 0] -} +fn make_procedurals() { + let base_color_fn = |x, _| { + let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); + let rgb: palette::LinSrgb = hsl.into_color(); + let r = (255.0 * rgb.red) as u8; + let g = (255.0 * rgb.green) as u8; + let b = (255.0 * rgb.blue) as u8; + let a = 255; + [r, g, b, a] + }; -fn default_metallic_roughness(_: f32, _: f32) -> [u8; 4] { - [255, 255, 0, 0] -} + let seed = 12345; + let num_trees = 15; -fn default_nearest_sampler() -> ProceduralSamplerHandle { - sampler::create(&sampler::Descriptor { + let base_color_map = make_texture(base_color_fn); + let normal_map = make_texture(|_, _| [128, 128, 255, 0]); + let metallic_roughness_map = make_texture(|_, _| [255, 255, 0, 0]); + let sampler = sampler::create(&sampler::Descriptor { address_mode_u: sampler::AddressMode::ClampToEdge, address_mode_v: sampler::AddressMode::ClampToEdge, address_mode_w: sampler::AddressMode::ClampToEdge, mag_filter: sampler::FilterMode::Nearest, min_filter: sampler::FilterMode::Nearest, mipmap_filter: sampler::FilterMode::Nearest, - }) -} - -fn make_procedural( - base_color_fn: BaseColorFn, - normal_fn: NormalFn, - metallic_roughness_fn: MetallicRoughnessFn, - sampler_fn: SamplerFn, - transparent: bool, -) where - BaseColorFn: FnMut(f32, f32) -> [u8; 4], - NormalFn: FnMut(f32, f32) -> [u8; 4], - MetallicRoughnessFn: FnMut(f32, f32) -> [u8; 4], - SamplerFn: FnOnce() -> ProceduralSamplerHandle, -{ - let mut vertices = vec![]; - let mut indices = vec![]; - for y in 0..=RESOLUTION_Y { - for x in 0..=RESOLUTION_X { - let px = SIZE_X * (x as f32) / (RESOLUTION_X as f32); - let py = SIZE_Y * (y as f32) / (RESOLUTION_Y as f32); - let pz = WAVE_AMPLITUDE * f32::sin(WAVE_FREQUENCY * px); - let u = (x as f32) / (RESOLUTION_X as f32); - let v = (y as f32) / (RESOLUTION_Y as f32); - vertices.push(mesh::Vertex { - position: vec3(px, py, pz) + vec3(-0.5 * SIZE_X, -0.5 * SIZE_Y, 0.0), - normal: vec3(0.0, 0.0, 1.0), - tangent: vec3(1.0, 0.0, 0.0), - texcoord0: vec2(u, v), - }); - } - } - for y in 0..RESOLUTION_Y { - for x in 0..RESOLUTION_X { - let i0 = x + y * (RESOLUTION_X + 1); - let i1 = (x + 1) + y * (RESOLUTION_X + 1); - let i2 = x + (y + 1) * (RESOLUTION_X + 1); - let i3 = (x + 1) + (y + 1) * (RESOLUTION_X + 1); - indices.extend([i0, i1, i2]); - indices.extend([i1, i3, i2]); - } - } - for triangle in indices.chunks_exact(3) { - let [i0, i1, i2]: [_; 3] = triangle.try_into().unwrap(); - let p0 = vertices[i0 as usize].position; - let p1 = vertices[i1 as usize].position; - let p2 = vertices[i2 as usize].position; - let n01 = (p1 - p0).normalize(); - let n02 = (p2 - p0).normalize(); - let n = n01.cross(n02).normalize(); - vertices[i0 as usize].normal += n; - vertices[i1 as usize].normal += n; - vertices[i2 as usize].normal += n; - } - for vertex in &mut vertices { - vertex.normal = vertex.normal.normalize(); - } - - let seed = 12345; - let num_trees = 15; - - let base_color_map = make_texture(base_color_fn); - let normal_map = make_texture(normal_fn); - let metallic_roughness_map = make_texture(metallic_roughness_fn); - let sampler = sampler_fn(); + }); let material = material::create(&material::Descriptor { base_color_map, normal_map, metallic_roughness_map, sampler, - transparent, + transparent: false, }); // lets plant some trees :) @@ -503,25 +443,6 @@ fn make_procedural( } } -fn make_procedurals() { - // Interpolated hue. - make_procedural( - |x, _| { - let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); - let rgb: palette::LinSrgb = hsl.into_color(); - let r = (255.0 * rgb.red) as u8; - let g = (255.0 * rgb.green) as u8; - let b = (255.0 * rgb.blue) as u8; - let a = 255; - [r, g, b, a] - }, - default_normal, - default_metallic_roughness, - default_nearest_sampler, - false, - ); -} - #[main] pub async fn main() { make_camera(); From 251860e561c8f91117e17e381a92917da9c9a796 Mon Sep 17 00:00:00 2001 From: Philpax Date: Thu, 25 May 2023 18:30:10 +0200 Subject: [PATCH 5/9] feat(forest): store tree data in the ECS --- .../examples/advanced/forest/ambient.toml | 16 +++ .../examples/advanced/forest/src/client.rs | 118 ++++++++++-------- 2 files changed, 85 insertions(+), 49 deletions(-) diff --git a/guest/rust/examples/advanced/forest/ambient.toml b/guest/rust/examples/advanced/forest/ambient.toml index a091fd1229..c9d5137fe1 100644 --- a/guest/rust/examples/advanced/forest/ambient.toml +++ b/guest/rust/examples/advanced/forest/ambient.toml @@ -5,3 +5,19 @@ version = "0.0.1" [components] rotating_sun = { type = "Bool", name = "Rotating sun", description = "Whether or not rotate the sun automatically" } +tree_seed = { type = "I32", name = "Tree seed", description = "The seed of the tree" } +tree_trunk_radius = { type = "F32", name = "Tree trunk radius", description = "The radius of the trunk" } +tree_trunk_height = { type = "F32", name = "Tree trunk height", description = "The height of the trunk" } +tree_trunk_segments = { type = "U32", name = "Tree trunk segments", description = "The number of segments of the trunk" } +tree_foliage_radius = { type = "F32", name = "Tree foliage radius", description = "The radius of the foliage" } +tree_foliage_density = { type = "U32", name = "Tree foliage density", description = "The number of foliage" } +tree_foliage_segments = { type = "U32", name = "Tree foliage segments", description = "The number of segments of the foliage" } + +[concepts.tree.components] +tree_seed = 123 +tree_trunk_radius = 3.0 +tree_trunk_height = 15.0 +tree_trunk_segments = 8 +tree_foliage_radius = 2.0 +tree_foliage_density = 5 +tree_foliage_segments = 5 \ No newline at end of file diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index 82fbcd94a6..f26fddbdf2 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -33,19 +33,6 @@ pub struct TreeMesh { pub foliage_segments: u32, } -impl Default for TreeMesh { - fn default() -> Self { - Self { - seed: 123, - trunk_radius: 3.0, - trunk_height: 15.0, - trunk_segments: 8, - foliage_radius: 2.0, - foliage_density: 5, - foliage_segments: 5, - } - } -} pub struct MeshDescriptor { vertices: Vec, indices: Vec, @@ -369,8 +356,8 @@ where }) } -fn make_procedurals() { - let base_color_fn = |x, _| { +fn register_tree_entity_augmentors() { + let base_color_map = make_texture(|x, _| { let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); let rgb: palette::LinSrgb = hsl.into_color(); let r = (255.0 * rgb.red) as u8; @@ -378,12 +365,7 @@ fn make_procedurals() { let b = (255.0 * rgb.blue) as u8; let a = 255; [r, g, b, a] - }; - - let seed = 12345; - let num_trees = 15; - - let base_color_map = make_texture(base_color_fn); + }); let normal_map = make_texture(|_, _| [128, 128, 255, 0]); let metallic_roughness_map = make_texture(|_, _| [255, 255, 0, 0]); let sampler = sampler::create(&sampler::Descriptor { @@ -402,43 +384,79 @@ fn make_procedurals() { transparent: false, }); + spawn_query(( + components::tree_seed(), + components::tree_foliage_density(), + components::tree_foliage_radius(), + components::tree_foliage_segments(), + components::tree_trunk_height(), + components::tree_trunk_radius(), + components::tree_trunk_segments(), + )) + .bind(move |trees| { + for ( + id, + ( + seed, + foliage_density, + foliage_radius, + foliage_segments, + trunk_height, + trunk_radius, + trunk_segments, + ), + ) in trees + { + let tree = create_tree(TreeMesh { + seed, + trunk_radius, + trunk_height, + trunk_segments, + foliage_radius, + foliage_density, + foliage_segments, + }); + let mesh = mesh::create(&mesh::Descriptor { + vertices: &tree.vertices, + indices: &tree.indices, + }); + + entity::add_components( + id, + Entity::new() + .with(procedural_mesh(), mesh) + .with(procedural_material(), material) + .with_default(cast_shadows()), + ); + } + }); +} + +fn make_trees() { + let seed = 12345; + let num_trees = 15; + // lets plant some trees :) for i in 0..num_trees { let trunk_radius = gen_rn(seed + i, 2.0, 3.0); let trunk_height = gen_rn(seed + i, 15.0, 20.0); let trunk_segments = gen_rn(seed + i, 6.0, 12.0) as u32; - let tree = TreeMesh { - seed, - trunk_radius, - trunk_height, - trunk_segments, - ..Default::default() - }; - - let tree = create_tree(tree); - - let td = mesh::Descriptor { - vertices: &tree.vertices, - indices: &tree.indices, - }; - - let mesh = mesh::create(&td); + let position = vec3( + gen_rn(seed + i, 0.0, 15.0), + gen_rn(seed + seed + i, 0.0, 15.0), + 0.0, + ); Entity::new() + .with_merge(concepts::make_tree()) .with_merge(make_transformable()) - .with(procedural_mesh(), mesh) - .with(procedural_material(), material) - .with_default(cast_shadows()) .with(scale(), Vec3::ONE * gen_rn(i, 0.2, 0.4)) - .with( - translation(), - vec3( - gen_rn(seed + i, 0.0, 15.0), - gen_rn(seed + seed + i, 0.0, 15.0), - 0.0, - ), - ) + .with(translation(), position) + .with(components::tree_seed(), seed + i) + .with(components::tree_trunk_radius(), trunk_radius) + .with(components::tree_trunk_height(), trunk_height) + .with(components::tree_trunk_segments(), trunk_segments) .spawn(); } } @@ -448,5 +466,7 @@ pub async fn main() { make_camera(); make_lighting(); make_ground(); - make_procedurals(); + + register_tree_entity_augmentors(); + make_trees(); } From c4e36318ee541f0f9e0fbedc6c6834d10e9eba1b Mon Sep 17 00:00:00 2001 From: mebyz Date: Fri, 26 May 2023 01:05:11 +0400 Subject: [PATCH 6/9] add branches --- .../examples/advanced/forest/ambient.toml | 6 + .../examples/advanced/forest/src/client.rs | 149 ++++++++++++++++-- 2 files changed, 143 insertions(+), 12 deletions(-) diff --git a/guest/rust/examples/advanced/forest/ambient.toml b/guest/rust/examples/advanced/forest/ambient.toml index c9d5137fe1..81b4f413e7 100644 --- a/guest/rust/examples/advanced/forest/ambient.toml +++ b/guest/rust/examples/advanced/forest/ambient.toml @@ -9,6 +9,9 @@ tree_seed = { type = "I32", name = "Tree seed", description = "The seed of the t tree_trunk_radius = { type = "F32", name = "Tree trunk radius", description = "The radius of the trunk" } tree_trunk_height = { type = "F32", name = "Tree trunk height", description = "The height of the trunk" } tree_trunk_segments = { type = "U32", name = "Tree trunk segments", description = "The number of segments of the trunk" } +tree_branch_length = { type = "F32", name = "Tree branches lenght", description = "The lenght of the branches" } +tree_branch_angle = { type = "F32", name = "Tree branches angle", description = "The angle of the branches" } +tree_branch_segments = { type = "U32", name = "Tree branch segments", description = "The number of segments of the branches" } tree_foliage_radius = { type = "F32", name = "Tree foliage radius", description = "The radius of the foliage" } tree_foliage_density = { type = "U32", name = "Tree foliage density", description = "The number of foliage" } tree_foliage_segments = { type = "U32", name = "Tree foliage segments", description = "The number of segments of the foliage" } @@ -18,6 +21,9 @@ tree_seed = 123 tree_trunk_radius = 3.0 tree_trunk_height = 15.0 tree_trunk_segments = 8 +tree_branch_length = 1.0 +tree_branch_angle = 1.0 +tree_branch_segments = 3 tree_foliage_radius = 2.0 tree_foliage_density = 5 tree_foliage_segments = 5 \ No newline at end of file diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index f26fddbdf2..46fe6709d0 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -28,6 +28,9 @@ pub struct TreeMesh { pub trunk_radius: f32, pub trunk_height: f32, pub trunk_segments: u32, + pub branch_length: f32, + pub branch_angle: f32, + pub branch_segments: u32, pub foliage_radius: f32, pub foliage_density: u32, pub foliage_segments: u32, @@ -40,7 +43,7 @@ pub struct MeshDescriptor { pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { // Create the trunk - let (mut vertices1, top_vertices1, mut normals1, mut uvs1) = build_trunk(&tree); + let (mut vertices1, top_vertices1, mut normals1, mut uvs1, trunk_direction) = build_trunk(&tree); let sectors = 12; let trunk_segments = tree.trunk_segments; @@ -62,10 +65,98 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { } } + + + // Generate branches + let branch_count = tree.branch_segments; + let branch_radius_variance = 0.02; + let branch_position_variance = vec3(0.8, 0.8, 0.7); + + let mut rng = ChaCha8Rng::seed_from_u64(tree.seed as u64); + + for i in 0..branch_count { + let branch_radius = 0.3; + //* (1.0 - rng.gen_range(0.0..1.0) * branch_radius_variance); + let mut branch_position = top_vertices1[rng.gen_range(0..top_vertices1.len())] + + vec3( + rng.gen_range(0.0..1.0) * branch_position_variance.x, + rng.gen_range(0.0..1.0) * branch_position_variance.y, + rng.gen_range(0.0..1.0) * branch_position_variance.z-1.0, + ); + + let segments = tree.branch_segments; + let sector_step = 2. * std::f32::consts::PI / segments as f32; + + let mut branch_vertices = Vec::new(); + let mut branch_normals = Vec::new(); + let mut branch_uvs = Vec::new(); + + let mut direction = vec3(0.0, 0.0, 1.0); + let direction_variance = 0.05; + + + // Get a random vertex from the top vertices of the trunk + let random_vertex_index = rng.gen_range(0..top_vertices1.len()); + let random_vertex = normals1[random_vertex_index]; + + // Calculate the initial direction of the branch from the chosen vertex + direction = (random_vertex - branch_position).normalize() + vec3(gen_rn(tree.seed + i as i32 + 4, -1.0, 1.0), gen_rn(tree.seed + i as i32 + 5, -1.0, 1.0), 0.0); + + for i in 0..=segments { + + let random_direction = vec3( + gen_rn(tree.seed + i as i32 + 1, 0.0, 1.0) - 0.5, + gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) - 0.5, + gen_rn(tree.seed + i as i32 + 3, 0.0, 1.0) - 0.5, + ) + .normalize() + * direction_variance; + direction = (direction + random_direction).normalize(); + + let theta = (i as f32 / segments as f32) * tree.branch_angle; + let height = branch_position.z + (tree.branch_length * theta.cos())*direction.z; + let segment_radius = branch_radius * theta.sin(); + + for j in 0..=sectors { + let phi = j as f32 * sector_step; + let x = branch_position.x + segment_radius * phi.cos(); + let y = branch_position.y + segment_radius * phi.sin(); + let z = height; + + branch_vertices.push(vec3(x, y, z)); + branch_normals.push( + vec3( + x - branch_position.x, + y - branch_position.y, + z - branch_position.z, + ) + .normalize(), + ); + branch_uvs.push(vec2(j as f32 / sectors as f32, i as f32 / segments as f32)); + } + branch_position = branch_position + + vec3( + rng.gen_range(-1.0..1.0) * branch_position_variance.x, + rng.gen_range(-1.0..1.0) * branch_position_variance.y, + rng.gen_range(0.0..1.0) * branch_position_variance.z, + ); + } + + let branch_indices = generate_branch_indices(segments as usize, sectors as usize); + + vertices1.extend(branch_vertices.clone()); + normals1.extend(branch_normals); + uvs1.extend(branch_uvs); + + let offset = vertices1.len() - branch_vertices.len(); + indices.extend(branch_indices.iter().map(|i| *i + offset as u32)); + } + + // Generate foliage let foliage_count = tree.foliage_density + tree.foliage_segments; let foliage_radius_variance = 0.05; - let foliage_position_variance = vec3(5.0, 5.0, 3.0); + let foliage_position_variance = vec3(3.0, 3.0, 3.0); for i in 0..foliage_count { let foliage_radius = tree.foliage_radius @@ -73,9 +164,9 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { let foliage_position = top_vertices1 [gen_rn(tree.seed, 0.0, top_vertices1.len() as f32) as usize] + vec3( - gen_rn(tree.seed + i as i32, 0.0, 1.0) * foliage_position_variance.x, - gen_rn(tree.seed + i as i32 + 1, 0.0, 1.0) * foliage_position_variance.y, - gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) * foliage_position_variance.z, + gen_rn(tree.seed + i as i32, -1.0, 1.0) * foliage_position_variance.x, + gen_rn(tree.seed + i as i32 + 1, -1.0, 1.0) * foliage_position_variance.y, + gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) * foliage_position_variance.z + 2.0, ); let segments = tree.foliage_segments; @@ -143,6 +234,29 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { indices } + // Function to generate indices for a branch based on segments and sectors + fn generate_branch_indices(segments: usize, sectors: usize) -> Vec { + let mut indices = Vec::with_capacity(segments * sectors * 6); + + for i in 0..segments { + for j in 0..sectors { + let index1 = i * (sectors + 1) + j; + let index2 = index1 + 1; + let index3 = (i + 1) * (sectors + 1) + j; + let index4 = index3 + 1; + + indices.push(index1 as u32); + indices.push(index2 as u32); + indices.push(index3 as u32); + + indices.push(index2 as u32); + indices.push(index4 as u32); + indices.push(index3 as u32); + } + } + indices + } + let mut vertices: Vec = Vec::with_capacity(vertices1.len()); for i in 0..vertices1.len() { @@ -167,7 +281,7 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { MeshDescriptor { vertices, indices } } -fn build_trunk(tree: &TreeMesh) -> (Vec, Vec, Vec, Vec) { +fn build_trunk(tree: &TreeMesh) -> (Vec, Vec, Vec, Vec, Vec3) { let sectors = 12; let sector_step = 2. * std::f32::consts::PI / sectors as f32; @@ -262,7 +376,7 @@ fn build_trunk(tree: &TreeMesh) -> (Vec, Vec, Vec, Vec) } } - (vertices, top_vertices, normals, uvs) + (vertices, top_vertices, normals, uvs, trunk_direction) } pub fn gen_rn(seed: i32, min: f32, max: f32) -> f32 { @@ -276,7 +390,7 @@ fn make_camera() { .with(aspect_ratio_from_window(), EntityId::resources()) .with_default(main_scene()) .with(translation(), vec3(10.0, 10.0, 4.0) * 2.0) - .with(lookat_target(), vec3(0.0, 3.0, 0.0)) + .with(lookat_target(), vec3(0.0, 0.0, 0.0)) .spawn(); } @@ -389,6 +503,8 @@ fn register_tree_entity_augmentors() { components::tree_foliage_density(), components::tree_foliage_radius(), components::tree_foliage_segments(), + components::tree_branch_length(), + components::tree_branch_angle(), components::tree_trunk_height(), components::tree_trunk_radius(), components::tree_trunk_segments(), @@ -401,6 +517,8 @@ fn register_tree_entity_augmentors() { foliage_density, foliage_radius, foliage_segments, + branch_length, + branch_angle, trunk_height, trunk_radius, trunk_segments, @@ -412,6 +530,9 @@ fn register_tree_entity_augmentors() { trunk_radius, trunk_height, trunk_segments, + branch_length, + branch_angle, + branch_segments:8, foliage_radius, foliage_density, foliage_segments, @@ -434,18 +555,20 @@ fn register_tree_entity_augmentors() { fn make_trees() { let seed = 12345; - let num_trees = 15; + let num_trees = 30; // lets plant some trees :) for i in 0..num_trees { let trunk_radius = gen_rn(seed + i, 2.0, 3.0); let trunk_height = gen_rn(seed + i, 15.0, 20.0); let trunk_segments = gen_rn(seed + i, 6.0, 12.0) as u32; + let branch_length = gen_rn(seed + i, 0.1, 0.3); + let branch_angle = gen_rn(seed + i, 10., 12.); let position = vec3( - gen_rn(seed + i, 0.0, 15.0), - gen_rn(seed + seed + i, 0.0, 15.0), - 0.0, + gen_rn(seed + i, -10.0, 15.0), + gen_rn(seed + seed + i, -10.0, 15.0), + -1.0, ); Entity::new() @@ -457,6 +580,8 @@ fn make_trees() { .with(components::tree_trunk_radius(), trunk_radius) .with(components::tree_trunk_height(), trunk_height) .with(components::tree_trunk_segments(), trunk_segments) + .with(components::tree_branch_length(), branch_length) + .with(components::tree_branch_angle(), branch_angle) .spawn(); } } From e90f37ab550339db356aa150b47909c0cc508dfd Mon Sep 17 00:00:00 2001 From: mebyz Date: Fri, 26 May 2023 22:12:56 +0400 Subject: [PATCH 7/9] tiles --- .../examples/advanced/forest/ambient.toml | 7 + .../examples/advanced/forest/src/client.rs | 161 +++++++++++++++++- 2 files changed, 165 insertions(+), 3 deletions(-) diff --git a/guest/rust/examples/advanced/forest/ambient.toml b/guest/rust/examples/advanced/forest/ambient.toml index 81b4f413e7..b7d442a283 100644 --- a/guest/rust/examples/advanced/forest/ambient.toml +++ b/guest/rust/examples/advanced/forest/ambient.toml @@ -5,6 +5,8 @@ version = "0.0.1" [components] rotating_sun = { type = "Bool", name = "Rotating sun", description = "Whether or not rotate the sun automatically" } +tile_seed = { type = "I32", name = "Tile seed", description = "The seed of the tile" } +tile_size = { type = "F32", name = "Tile seed", description = "The size of the tree" } tree_seed = { type = "I32", name = "Tree seed", description = "The seed of the tree" } tree_trunk_radius = { type = "F32", name = "Tree trunk radius", description = "The radius of the trunk" } tree_trunk_height = { type = "F32", name = "Tree trunk height", description = "The height of the trunk" } @@ -16,6 +18,11 @@ tree_foliage_radius = { type = "F32", name = "Tree foliage radius", description tree_foliage_density = { type = "U32", name = "Tree foliage density", description = "The number of foliage" } tree_foliage_segments = { type = "U32", name = "Tree foliage segments", description = "The number of segments of the foliage" } + +[concepts.tile.components] +tile_seed = 123 +tile_size = 1.0 + [concepts.tree.components] tree_seed = 123 tree_trunk_radius = 3.0 diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index 46fe6709d0..85a36184cf 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -470,7 +470,7 @@ where }) } -fn register_tree_entity_augmentors() { +fn register_augmentors() { let base_color_map = make_texture(|x, _| { let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); let rgb: palette::LinSrgb = hsl.into_color(); @@ -551,6 +551,34 @@ fn register_tree_entity_augmentors() { ); } }); + + spawn_query(( + components::tile_seed(), + components::tile_size(), + )) + .bind(move |tiles| { + for ( + id, + ( + seed, size + ), + ) in tiles + { + let tile = create_tile(GridMesh { top_left: Vec2 { x: 0.0, y: 0.0 }, size: Vec2 { x: size, y: size }, n_vertices_width: 10, n_vertices_height: 10, uv_min: Vec2 { x: 0.0, y: 0.0 } , uv_max: Vec2 { x: 0.0, y: 0.0 }, normal: Vec3 { x: 0.0, y: 0.0, z: 1.0 } }); + let mesh = mesh::create(&mesh::Descriptor { + vertices: &tile.vertices, + indices: &tile.indices, + }); + + entity::add_components( + id, + Entity::new() + .with(procedural_mesh(), mesh) + .with(procedural_material(), material) + //.with_default(cast_shadows()), + ); + } + }); } fn make_trees() { @@ -571,7 +599,7 @@ fn make_trees() { -1.0, ); - Entity::new() + let id = Entity::new() .with_merge(concepts::make_tree()) .with_merge(make_transformable()) .with(scale(), Vec3::ONE * gen_rn(i, 0.2, 0.4)) @@ -586,12 +614,139 @@ fn make_trees() { } } +fn make_tiles() { + let num_tiles_x = 10; + let num_tiles_y = 10; + let size = Vec2::ONE * 1.0; + + for num_tile_x in 0..num_tiles_x { + for num_tile_y in 0..num_tiles_y { + let position = vec3( + num_tile_x as f32 * 2.0, + num_tile_y as f32 * 2.0, + 0.001, + ); + + let id = Entity::new() + .with_merge(concepts::make_tile()) + .with_merge(make_transformable()) + .with(translation(), position) + .with(components::tile_size(), size) + .spawn(); + } + } +} + + + +#[derive(Debug, Clone)] +pub struct GridMesh { + pub top_left: glam::Vec2, + pub size: glam::Vec2, + pub n_vertices_width: usize, + pub n_vertices_height: usize, + pub uv_min: glam::Vec2, + pub uv_max: glam::Vec2, + pub normal: glam::Vec3, +} + +impl Default for GridMesh { + fn default() -> GridMesh { + GridMesh { + top_left: glam::Vec2::ZERO, + size: glam::Vec2::ONE, + n_vertices_width: 2, + n_vertices_height: 2, + uv_min: glam::Vec2::ZERO, + uv_max: glam::Vec2::ONE, + normal: glam::Vec3::Z, + } + } +} + + +pub fn create_tile(grid: GridMesh) -> MeshDescriptor { + // Create the tile + let (mut vertices1, mut uvs1, mut normals1, mut indices) = build_tile(&grid); + + let mut vertices: Vec = Vec::with_capacity(vertices1.len()); + + for i in 0..vertices1.len() { + let px = vertices1[i].x; + let py = vertices1[i].y; + let pz = vertices1[i].z; + let u = uvs1[i].x; + let v = uvs1[i].y; + let nx = normals1[i].x; + let ny = normals1[i].y; + let nz = normals1[i].z; + + let v = mesh::Vertex { + position: vec3(px, py, pz) + vec3(-0.5 * SIZE_X, -0.5 * SIZE_Y, 0.0), + normal: vec3(nx, ny, nz), + tangent: vec3(1.0, 0.0, 0.0), + texcoord0: vec2(u, v), + }; + vertices.push(v); + } + + MeshDescriptor{ vertices, indices } + +} + +pub fn build_tile(grid: &GridMesh) -> (Vec, Vec, Vec, Vec) { + let mut positions = Vec::new(); + let mut texcoords = Vec::new(); + let mut normals = Vec::new(); + let mut indices = Vec::new(); + for y in 0..grid.n_vertices_height { + for x in 0..grid.n_vertices_width { + let p = glam::Vec2::new( + x as f32 / (grid.n_vertices_width as f32 - 1.0), + y as f32 / (grid.n_vertices_height as f32 - 1.0), + ); + positions.push(vec3( + grid.top_left.x + grid.size.x * p.x, + grid.top_left.y + grid.size.y * p.y, + 0., + )); + texcoords.push(vec2( + grid.uv_min.x + (grid.uv_max.x - grid.uv_min.x) * p.x, + grid.uv_min.y + (grid.uv_max.y - grid.uv_min.y) * p.y, + )); + let normal = grid.normal; + normals.push(vec3(normal.x, normal.y, normal.z)); + if y < grid.n_vertices_height - 1 && x < grid.n_vertices_width - 1 { + let vert_index = x + y * grid.n_vertices_width; + indices.push((vert_index) as u32); + indices.push((vert_index + 1) as u32); + indices.push((vert_index + grid.n_vertices_width) as u32); + + indices.push((vert_index + 1) as u32); + indices.push((vert_index + grid.n_vertices_width + 1) as u32); + indices.push((vert_index + grid.n_vertices_width) as u32); + } + } + } + + ( + positions, + texcoords, + normals, + indices, + + ) + +} + #[main] pub async fn main() { make_camera(); make_lighting(); make_ground(); - register_tree_entity_augmentors(); + register_augmentors(); make_trees(); + make_tiles(); + } From 6a4838cd5e30a4b2216c2e860b49d3519bbfdf97 Mon Sep 17 00:00:00 2001 From: mebyz Date: Fri, 26 May 2023 22:18:11 +0400 Subject: [PATCH 8/9] clean --- .../examples/advanced/forest/src/client.rs | 104 ++++++++---------- 1 file changed, 45 insertions(+), 59 deletions(-) diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index 85a36184cf..994a2acaac 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -43,7 +43,8 @@ pub struct MeshDescriptor { pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { // Create the trunk - let (mut vertices1, top_vertices1, mut normals1, mut uvs1, trunk_direction) = build_trunk(&tree); + let (mut vertices1, top_vertices1, mut normals1, mut uvs1, trunk_direction) = + build_trunk(&tree); let sectors = 12; let trunk_segments = tree.trunk_segments; @@ -65,8 +66,6 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { } } - - // Generate branches let branch_count = tree.branch_segments; let branch_radius_variance = 0.02; @@ -76,12 +75,12 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { for i in 0..branch_count { let branch_radius = 0.3; - //* (1.0 - rng.gen_range(0.0..1.0) * branch_radius_variance); + //* (1.0 - rng.gen_range(0.0..1.0) * branch_radius_variance); let mut branch_position = top_vertices1[rng.gen_range(0..top_vertices1.len())] + vec3( rng.gen_range(0.0..1.0) * branch_position_variance.x, rng.gen_range(0.0..1.0) * branch_position_variance.y, - rng.gen_range(0.0..1.0) * branch_position_variance.z-1.0, + rng.gen_range(0.0..1.0) * branch_position_variance.z - 1.0, ); let segments = tree.branch_segments; @@ -94,27 +93,30 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { let mut direction = vec3(0.0, 0.0, 1.0); let direction_variance = 0.05; - // Get a random vertex from the top vertices of the trunk let random_vertex_index = rng.gen_range(0..top_vertices1.len()); let random_vertex = normals1[random_vertex_index]; // Calculate the initial direction of the branch from the chosen vertex - direction = (random_vertex - branch_position).normalize() + vec3(gen_rn(tree.seed + i as i32 + 4, -1.0, 1.0), gen_rn(tree.seed + i as i32 + 5, -1.0, 1.0), 0.0); + direction = (random_vertex - branch_position).normalize() + + vec3( + gen_rn(tree.seed + i as i32 + 4, -1.0, 1.0), + gen_rn(tree.seed + i as i32 + 5, -1.0, 1.0), + 0.0, + ); for i in 0..=segments { - - let random_direction = vec3( - gen_rn(tree.seed + i as i32 + 1, 0.0, 1.0) - 0.5, - gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) - 0.5, - gen_rn(tree.seed + i as i32 + 3, 0.0, 1.0) - 0.5, - ) - .normalize() - * direction_variance; - direction = (direction + random_direction).normalize(); + let random_direction = vec3( + gen_rn(tree.seed + i as i32 + 1, 0.0, 1.0) - 0.5, + gen_rn(tree.seed + i as i32 + 2, 0.0, 1.0) - 0.5, + gen_rn(tree.seed + i as i32 + 3, 0.0, 1.0) - 0.5, + ) + .normalize() + * direction_variance; + direction = (direction + random_direction).normalize(); let theta = (i as f32 / segments as f32) * tree.branch_angle; - let height = branch_position.z + (tree.branch_length * theta.cos())*direction.z; + let height = branch_position.z + (tree.branch_length * theta.cos()) * direction.z; let segment_radius = branch_radius * theta.sin(); for j in 0..=sectors { @@ -135,11 +137,11 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { branch_uvs.push(vec2(j as f32 / sectors as f32, i as f32 / segments as f32)); } branch_position = branch_position - + vec3( - rng.gen_range(-1.0..1.0) * branch_position_variance.x, - rng.gen_range(-1.0..1.0) * branch_position_variance.y, - rng.gen_range(0.0..1.0) * branch_position_variance.z, - ); + + vec3( + rng.gen_range(-1.0..1.0) * branch_position_variance.x, + rng.gen_range(-1.0..1.0) * branch_position_variance.y, + rng.gen_range(0.0..1.0) * branch_position_variance.z, + ); } let branch_indices = generate_branch_indices(segments as usize, sectors as usize); @@ -152,7 +154,6 @@ pub fn create_tree(tree: TreeMesh) -> MeshDescriptor { indices.extend(branch_indices.iter().map(|i| *i + offset as u32)); } - // Generate foliage let foliage_count = tree.foliage_density + tree.foliage_segments; let foliage_radius_variance = 0.05; @@ -532,7 +533,7 @@ fn register_augmentors() { trunk_segments, branch_length, branch_angle, - branch_segments:8, + branch_segments: 8, foliage_radius, foliage_density, foliage_segments, @@ -552,19 +553,21 @@ fn register_augmentors() { } }); - spawn_query(( - components::tile_seed(), - components::tile_size(), - )) - .bind(move |tiles| { - for ( - id, - ( - seed, size - ), - ) in tiles - { - let tile = create_tile(GridMesh { top_left: Vec2 { x: 0.0, y: 0.0 }, size: Vec2 { x: size, y: size }, n_vertices_width: 10, n_vertices_height: 10, uv_min: Vec2 { x: 0.0, y: 0.0 } , uv_max: Vec2 { x: 0.0, y: 0.0 }, normal: Vec3 { x: 0.0, y: 0.0, z: 1.0 } }); + spawn_query((components::tile_seed(), components::tile_size())).bind(move |tiles| { + for (id, (seed, size)) in tiles { + let tile = create_tile(GridMesh { + top_left: Vec2 { x: 0.0, y: 0.0 }, + size: Vec2 { x: size, y: size }, + n_vertices_width: 10, + n_vertices_height: 10, + uv_min: Vec2 { x: 0.0, y: 0.0 }, + uv_max: Vec2 { x: 0.0, y: 0.0 }, + normal: Vec3 { + x: 0.0, + y: 0.0, + z: 1.0, + }, + }); let mesh = mesh::create(&mesh::Descriptor { vertices: &tile.vertices, indices: &tile.indices, @@ -574,8 +577,7 @@ fn register_augmentors() { id, Entity::new() .with(procedural_mesh(), mesh) - .with(procedural_material(), material) - //.with_default(cast_shadows()), + .with(procedural_material(), material), //.with_default(cast_shadows()), ); } }); @@ -621,11 +623,7 @@ fn make_tiles() { for num_tile_x in 0..num_tiles_x { for num_tile_y in 0..num_tiles_y { - let position = vec3( - num_tile_x as f32 * 2.0, - num_tile_y as f32 * 2.0, - 0.001, - ); + let position = vec3(num_tile_x as f32 * 2.0, num_tile_y as f32 * 2.0, 0.001); let id = Entity::new() .with_merge(concepts::make_tile()) @@ -637,8 +635,6 @@ fn make_tiles() { } } - - #[derive(Debug, Clone)] pub struct GridMesh { pub top_left: glam::Vec2, @@ -664,7 +660,6 @@ impl Default for GridMesh { } } - pub fn create_tile(grid: GridMesh) -> MeshDescriptor { // Create the tile let (mut vertices1, mut uvs1, mut normals1, mut indices) = build_tile(&grid); @@ -690,11 +685,10 @@ pub fn create_tile(grid: GridMesh) -> MeshDescriptor { vertices.push(v); } - MeshDescriptor{ vertices, indices } - + MeshDescriptor { vertices, indices } } -pub fn build_tile(grid: &GridMesh) -> (Vec, Vec, Vec, Vec) { +pub fn build_tile(grid: &GridMesh) -> (Vec, Vec, Vec, Vec) { let mut positions = Vec::new(); let mut texcoords = Vec::new(); let mut normals = Vec::new(); @@ -729,14 +723,7 @@ pub fn build_tile(grid: &GridMesh) -> (Vec, Vec, Vec, Vec Date: Sat, 27 May 2023 10:46:50 +0400 Subject: [PATCH 9/9] tiles --- .../examples/advanced/forest/ambient.toml | 4 ++ .../examples/advanced/forest/src/client.rs | 55 ++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/guest/rust/examples/advanced/forest/ambient.toml b/guest/rust/examples/advanced/forest/ambient.toml index b7d442a283..9bdcfaac54 100644 --- a/guest/rust/examples/advanced/forest/ambient.toml +++ b/guest/rust/examples/advanced/forest/ambient.toml @@ -6,6 +6,8 @@ version = "0.0.1" [components] rotating_sun = { type = "Bool", name = "Rotating sun", description = "Whether or not rotate the sun automatically" } tile_seed = { type = "I32", name = "Tile seed", description = "The seed of the tile" } +tile_x = { type = "I32", name = "Tile x", description = "The x index of the tile" } +tile_y = { type = "I32", name = "Tile y", description = "The y index of the tile" } tile_size = { type = "F32", name = "Tile seed", description = "The size of the tree" } tree_seed = { type = "I32", name = "Tree seed", description = "The seed of the tree" } tree_trunk_radius = { type = "F32", name = "Tree trunk radius", description = "The radius of the trunk" } @@ -22,6 +24,8 @@ tree_foliage_segments = { type = "U32", name = "Tree foliage segments", descript [concepts.tile.components] tile_seed = 123 tile_size = 1.0 +tile_x = 0 +tile_y = 0 [concepts.tree.components] tree_seed = 123 diff --git a/guest/rust/examples/advanced/forest/src/client.rs b/guest/rust/examples/advanced/forest/src/client.rs index 994a2acaac..86828bf065 100644 --- a/guest/rust/examples/advanced/forest/src/client.rs +++ b/guest/rust/examples/advanced/forest/src/client.rs @@ -472,6 +472,9 @@ where } fn register_augmentors() { + let mut rng = rand_pcg::Pcg64::seed_from_u64(0); + let dist_zero_to_255 = rand::distributions::Uniform::new_inclusive(0_u8, 255_u8); + let base_color_map = make_texture(|x, _| { let hsl = palette::Hsl::new(360.0 * x, 1.0, 0.5).into_format::(); let rgb: palette::LinSrgb = hsl.into_color(); @@ -481,6 +484,13 @@ fn register_augmentors() { let a = 255; [r, g, b, a] }); + let base_color_map2 = make_texture(|x, _| { + let r = 255; + let g = 255; + let b = 255; + let a = dist_zero_to_255.sample(&mut rng); + [r, g, b, a] + }); let normal_map = make_texture(|_, _| [128, 128, 255, 0]); let metallic_roughness_map = make_texture(|_, _| [255, 255, 0, 0]); let sampler = sampler::create(&sampler::Descriptor { @@ -498,6 +508,13 @@ fn register_augmentors() { sampler, transparent: false, }); + let material2 = material::create(&material::Descriptor { + base_color_map: base_color_map2, + normal_map, + metallic_roughness_map, + sampler, + transparent: false, + }); spawn_query(( components::tree_seed(), @@ -553,10 +570,15 @@ fn register_augmentors() { } }); - spawn_query((components::tile_seed(), components::tile_size())).bind(move |tiles| { - for (id, (seed, size)) in tiles { + spawn_query( + (components::tile_seed(), + components::tile_size(), + components::tile_x(), + components::tile_y(), + )).bind(move |tiles| { + for (id, (seed, size, tile_x, tile_y)) in tiles { let tile = create_tile(GridMesh { - top_left: Vec2 { x: 0.0, y: 0.0 }, + top_left: Vec2 { x: tile_x as f32 * size, y: tile_y as f32 * size}, size: Vec2 { x: size, y: size }, n_vertices_width: 10, n_vertices_height: 10, @@ -577,7 +599,9 @@ fn register_augmentors() { id, Entity::new() .with(procedural_mesh(), mesh) - .with(procedural_material(), material), //.with_default(cast_shadows()), + .with(color(), vec4(0.25, 1.0, 0.25, 1.0)) + .with(procedural_material(), material) + .with_default(cast_shadows()), ); } }); @@ -617,19 +641,23 @@ fn make_trees() { } fn make_tiles() { - let num_tiles_x = 10; - let num_tiles_y = 10; - let size = Vec2::ONE * 1.0; + let num_tiles_x = 30; + let num_tiles_y = 30; + let size = 10.0; + let seed = 123; for num_tile_x in 0..num_tiles_x { for num_tile_y in 0..num_tiles_y { - let position = vec3(num_tile_x as f32 * 2.0, num_tile_y as f32 * 2.0, 0.001); + let position = vec3(num_tile_x as f32 * 1.0-20.0, num_tile_y as f32 * 1.0-20.0, 0.001); let id = Entity::new() .with_merge(concepts::make_tile()) .with_merge(make_transformable()) .with(translation(), position) + .with(components::tile_seed(), seed + num_tile_x + num_tile_y * num_tiles_x) .with(components::tile_size(), size) + .with(components::tile_x(), num_tile_x) + .with(components::tile_y(), num_tile_y) .spawn(); } } @@ -702,7 +730,7 @@ pub fn build_tile(grid: &GridMesh) -> (Vec, Vec, Vec, Vec positions.push(vec3( grid.top_left.x + grid.size.x * p.x, grid.top_left.y + grid.size.y * p.y, - 0., + get_height((grid.top_left.x + grid.size.x * p.x) as i32, (grid.top_left.y + grid.size.y * p.y) as i32) )); texcoords.push(vec2( grid.uv_min.x + (grid.uv_max.x - grid.uv_min.x) * p.x, @@ -726,6 +754,15 @@ pub fn build_tile(grid: &GridMesh) -> (Vec, Vec, Vec, Vec (positions, texcoords, normals, indices) } +fn get_height(x:i32, y:i32) -> f32 { + let x = x as f32; + let y = y as f32; + // perlin noise without crate + let noise = (x.sin() + y.cos()) * 0.5; + let height = noise * 0.5 + 0.5; + height * 2.0 +} + #[main] pub async fn main() { make_camera();