use super::components::TerrainConfig;
use super::generation::{TerrainGenerationResult, generate_terrain_mesh};
use crate::ecs::bounding_volume::components::{BoundingVolume, OrientedBoundingBox};
use crate::ecs::material::components::{Material, MaterialRef};
use crate::ecs::material::resources::material_registry_insert;
use crate::ecs::mesh::components::RenderMesh;
use crate::ecs::physics::components::{ColliderComponent, ColliderShape, RigidBodyComponent};
use crate::ecs::physics::types::InteractionGroups;
use crate::ecs::prefab::resources::mesh_cache_insert;
use crate::ecs::shadow::components::CastsShadow;
use crate::ecs::transform::components::LocalTransform;
use crate::ecs::visibility::components::Visibility;
use crate::ecs::world::{
BOUNDING_VOLUME, CASTS_SHADOW, COLLIDER, Entity, GLOBAL_TRANSFORM, LOCAL_TRANSFORM,
LOCAL_TRANSFORM_DIRTY, MATERIAL_REF, NAME, RENDER_MESH, RIGID_BODY, VISIBILITY, World,
};
use nalgebra_glm::{Quat, Vec3};
pub fn spawn_terrain(world: &mut World, config: TerrainConfig, position: Vec3) -> Entity {
let TerrainGenerationResult {
mesh,
heights,
min_height,
max_height,
} = generate_terrain_mesh(&config);
let mesh_name = format!("Terrain_{}", rand::random::<u32>());
mesh_cache_insert(&mut world.resources.mesh_cache, mesh_name.clone(), mesh);
let entity = world.spawn_entities(
NAME | LOCAL_TRANSFORM
| GLOBAL_TRANSFORM
| LOCAL_TRANSFORM_DIRTY
| RENDER_MESH
| MATERIAL_REF
| BOUNDING_VOLUME
| CASTS_SHADOW
| RIGID_BODY
| COLLIDER
| VISIBILITY,
1,
)[0];
if let Some(name) = world.get_name_mut(entity) {
name.0 = "Terrain".to_string();
}
world.set_local_transform(
entity,
LocalTransform {
translation: position,
rotation: Quat::identity(),
scale: Vec3::new(1.0, 1.0, 1.0),
},
);
if let Some(render_mesh) = world.get_render_mesh_mut(entity) {
*render_mesh = RenderMesh::new(&mesh_name);
}
if let Some(&index) = world
.resources
.mesh_cache
.registry
.name_to_index
.get(&mesh_name)
{
world.resources.mesh_cache.registry.add_reference(index);
}
let heightfield_heights = convert_heights_to_heightfield(
&heights,
config.resolution_x as usize,
config.resolution_z as usize,
);
if let Some(rigid_body) = world.get_rigid_body_mut(entity) {
*rigid_body =
RigidBodyComponent::new_static().with_translation(position.x, position.y, position.z);
}
if let Some(collider) = world.get_collider_mut(entity) {
*collider = ColliderComponent {
handle: None,
shape: ColliderShape::HeightField {
nrows: config.resolution_z as usize,
ncols: config.resolution_x as usize,
heights: heightfield_heights,
scale: [config.width, 1.0, config.depth],
},
friction: 0.9,
restitution: 0.0,
density: 0.0,
is_sensor: false,
collision_groups: InteractionGroups::all(),
solver_groups: InteractionGroups::all(),
};
}
if let Some(bv) = world.get_bounding_volume_mut(entity) {
let half_width = config.width / 2.0;
let half_depth = config.depth / 2.0;
let half_height = (max_height - min_height) / 2.0;
let center_y = (max_height + min_height) / 2.0;
let sphere_radius =
(half_width * half_width + half_height * half_height + half_depth * half_depth).sqrt();
*bv = BoundingVolume {
obb: OrientedBoundingBox::new(
Vec3::new(0.0, center_y, 0.0),
Vec3::new(half_width, half_height, half_depth),
Quat::identity(),
),
sphere_radius,
};
}
world.set_casts_shadow(entity, CastsShadow);
world.set_visibility(entity, Visibility { visible: true });
entity
}
pub fn spawn_terrain_with_material(
world: &mut World,
config: TerrainConfig,
position: Vec3,
material: Material,
) -> Entity {
let entity = spawn_terrain(world, config, position);
let material_name = format!("TerrainMaterial_{}", entity.id);
material_registry_insert(
&mut world.resources.material_registry,
material_name.clone(),
material,
);
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world.set_material_ref(entity, MaterialRef::new(material_name));
entity
}
fn convert_heights_to_heightfield(
heights: &[f32],
resolution_x: usize,
resolution_z: usize,
) -> Vec<f32> {
let mut heightfield_heights = vec![0.0; heights.len()];
for z in 0..resolution_z {
for x in 0..resolution_x {
let mesh_index = z * resolution_x + x;
let heightfield_index = z + resolution_z * x;
heightfield_heights[heightfield_index] = heights[mesh_index];
}
}
heightfield_heights
}