mirror of
https://github.com/eliasstepanik/voxel-simulation.git
synced 2026-01-12 14:28:28 +00:00
116 lines
3.8 KiB
Rust
116 lines
3.8 KiB
Rust
use bevy::asset::RenderAssetUsages;
|
||
use bevy::pbr::wireframe::{Wireframe, WireframeColor};
|
||
use bevy::prelude::*;
|
||
use bevy::render::mesh::*;
|
||
use big_space::floating_origins::FloatingOrigin;
|
||
use big_space::prelude::GridCell;
|
||
use noise::{Fbm, NoiseFn, Perlin};
|
||
use crate::plugins::big_space::big_space_plugin::RootGrid;
|
||
use crate::plugins::environment::systems::camera_system::CameraController;
|
||
|
||
|
||
#[derive(Component)]
|
||
pub struct PlanetMaker;
|
||
|
||
|
||
|
||
#[derive(Resource)]
|
||
pub struct PlanetNoise(pub Fbm<Perlin>);
|
||
|
||
pub fn setup(
|
||
mut commands: Commands,
|
||
mut meshes: ResMut<Assets<Mesh>>,
|
||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||
root: Res<RootGrid>,
|
||
) {
|
||
// Diameter ~ Earth (~12,742 km) × 2 to exaggerate terrain if desired
|
||
let radius = 12_742_000.0;
|
||
let sphere_mesh = meshes.add(
|
||
SphereMeshBuilder::new(radius, SphereKind::Ico { subdivisions: 100 })
|
||
.build(),
|
||
);
|
||
let material_handle = materials.add(StandardMaterial::from(Color::rgb(0.3, 0.6, 1.0)));
|
||
|
||
commands.entity(root.0).with_children(|parent| {
|
||
parent.spawn((
|
||
Name::new("Planet"),
|
||
Mesh3d(sphere_mesh.clone()),
|
||
MeshMaterial3d(material_handle),
|
||
GridCell::<i64>::ZERO,
|
||
Transform::default(),
|
||
PlanetMaker,
|
||
Wireframe,
|
||
));
|
||
});
|
||
}
|
||
|
||
|
||
pub(crate) fn setup_noise(mut commands: Commands) {
|
||
let fbm_noise = Fbm::<Perlin>::new(0);
|
||
commands.insert_resource(PlanetNoise(fbm_noise));
|
||
}
|
||
|
||
pub fn deform_planet(
|
||
mut meshes: ResMut<Assets<Mesh>>,
|
||
noise: Res<PlanetNoise>,
|
||
query: Query<&Mesh3d, With<PlanetMaker>>,
|
||
) {
|
||
let frequency = 4.0 / 12_742_000.0;
|
||
let amplitude = 100_000.0;
|
||
|
||
for mesh3d in query.iter() {
|
||
let handle: &Handle<Mesh> = &mesh3d.0;
|
||
|
||
if let Some(mesh) = meshes.get_mut(handle) {
|
||
// 1. Immutable borrow to extract normals (or default)
|
||
let normals: Vec<[f32; 3]> = if let Some(VertexAttributeValues::Float32x3(vals)) =
|
||
mesh.attribute(Mesh::ATTRIBUTE_NORMAL)
|
||
{
|
||
vals.clone()
|
||
} else {
|
||
// default normals if none exist
|
||
let count = mesh
|
||
.attribute(Mesh::ATTRIBUTE_POSITION)
|
||
.and_then(|attr| match attr {
|
||
VertexAttributeValues::Float32x3(v) => Some(v.len()),
|
||
_ => None,
|
||
})
|
||
.unwrap_or(0);
|
||
vec![[0.0, 1.0, 0.0]; count]
|
||
};
|
||
|
||
// 2. Drop the immutable borrow, then mutable-borrow positions
|
||
if let Some(VertexAttributeValues::Float32x3(positions)) =
|
||
mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)
|
||
{
|
||
// Now mutate positions using the pre-fetched normals
|
||
for (i, pos) in positions.iter_mut().enumerate() {
|
||
let mut vertex = Vec3::new(pos[0], pos[1], pos[2]);
|
||
let normal = Vec3::new(
|
||
normals[i][0],
|
||
normals[i][1],
|
||
normals[i][2],
|
||
);
|
||
|
||
let unit_dir = vertex.normalize();
|
||
let sample = [
|
||
unit_dir.x as f64 * frequency as f64,
|
||
unit_dir.y as f64 * frequency as f64,
|
||
unit_dir.z as f64 * frequency as f64,
|
||
];
|
||
let noise_value = noise.0.get(sample) as f32;
|
||
let offset = normal * (noise_value * amplitude);
|
||
|
||
let new_pos = unit_dir * (vertex.length() + offset.length());
|
||
*pos = [new_pos.x, new_pos.y, new_pos.z];
|
||
}
|
||
|
||
|
||
mesh.compute_smooth_normals();
|
||
}
|
||
|
||
// Force AABB recalc
|
||
mesh.remove_attribute(Mesh::ATTRIBUTE_COLOR);
|
||
}
|
||
}
|
||
} |