use core::f32; use std::time::Duration; use anyhow::Result; use ecs::*; use engine::prelude::{ cgmath::{Matrix4, Vector3}, *, }; use hexasphere::shapes::IcoSphere; #[derive(Clone, Copy, Debug)] pub enum CelestialClass { Sun, Solid, Gas, } impl CelestialClass { /// in g/cm³ pub fn density(&self) -> f32 { match self { // our sun CelestialClass::Sun => 1.41, // earth CelestialClass::Solid => 5.51, // saturn CelestialClass::Gas => 0.69, } } pub fn color(&self) -> [f32; 4] { match self { CelestialClass::Sun => Color::Yellow.into(), CelestialClass::Solid => Color::Green.into(), CelestialClass::Gas => Color::Orange.into(), } } } pub struct CelestialObject; impl CelestialObject { pub fn new( world: &mut World, class: CelestialClass, subdivisions: usize, celestial_reference: impl Into>, ) -> Result { let mut entity_object = AssetHandler::create(world).empty_entity(); let mut celestial_settings = CelestialObjectSettings::default(); celestial_settings.density = class.density(); let (mesh, bounding_box) = Self::create_asset(world, class, subdivisions)?; let draw = Draw::new(vec![mesh]); draw.set_transform(celestial_settings.model_matrix())?; entity_object.insert_component(draw); entity_object.insert_component(bounding_box); entity_object.insert_component(celestial_settings); if let Some(e) = celestial_reference.into() { entity_object.insert_component(CelestialReference { entity: e }); } Ok(entity_object) } fn create_asset( world: &World, class: CelestialClass, subdivisions: usize, ) -> Result<(AssetMesh, BoundingBox)> { let context = world.resources.get::(); let scene = world.resources.get::(); let command_buffer = CommandBuffer::new_primary() .build(context.device().clone(), context.queue().clone())?; let (sphere, bounding_box) = Self::create_sphere(subdivisions); let buffer = { let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, ))?; Buffer::builder() .set_memory_usage(MemoryUsage::CpuToGpu) .set_usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) .set_data( &sphere .into_iter() .map(|(v, n)| PositionNormal::new(v, n)) .collect::>(), ) .build(context.device().clone())? .into_device_local( &mut recorder, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, )? }; // submit let submit = SubmitInfo::default().add_command_buffer(&command_buffer); let fence = Fence::builder().build(context.device().clone())?; context .queue() .lock() .unwrap() .submit(Some(&fence), &[submit])?; context .device() .wait_for_fences(&[&fence], true, Duration::from_secs(1))?; let mut asset_mesh = AssetMesh::new(context.device(), scene.render_type())?; asset_mesh.add_primitive( buffer, None, None, PrimitiveMaterial { color: class.color(), emissive_factor: [0.65, 0.65, 0.65], metallic_factor: 0.5, roughness_factor: 0.7, ..PrimitiveMaterial::default() }, false, )?; Ok((asset_mesh, bounding_box)) } fn create_sphere(subdivisions: usize) -> (Vec<(Vector3, Vector3)>, BoundingBox) { let mut bounding_box = BoundingBox::init(); let sphere = IcoSphere::new(subdivisions, |_| ()); let points = sphere .raw_points() .into_iter() .map(|v| { let v = Vector3::from(v.to_array()); bounding_box.check(v); v }) .collect::>>(); ( sphere .get_all_indices() .chunks(3) .map(|triangle| { let p1 = points[triangle[0] as usize]; let p2 = points[triangle[1] as usize]; let p3 = points[triangle[2] as usize]; let v1 = p1 - p2; let v2 = p1 - p3; let n = v2.cross(v1); [(p1, n), (p2, n), (p3, n)] }) .flatten() .collect(), bounding_box, ) } } #[derive(Debug)] pub struct CelestialObjectSettings { pub location: Vector3, pub radius: f32, pub density: f32, pub velocity: f32, last_update: Duration, } impl CelestialObjectSettings { const SCALE: f32 = 0.5; fn volume(&self) -> f32 { (4.0 / 3.0) * f32::consts::PI * self.radius.powi(3) } fn mass(&self) -> f32 { self.volume() * self.density } pub fn model_matrix(&self) -> Matrix4 { Matrix4::from_translation(self.location) * Matrix4::from_scale(self.radius * Self::SCALE) } pub fn update( &mut self, now: Duration, draw: &Draw, celestial_object_settings_reference: &CelestialObjectSettings, ) -> Result<()> { self.last_update = now; // TODO: velocity calculation around celestial reference Ok(()) } } impl Default for CelestialObjectSettings { fn default() -> Self { Self { location: Vector3::zero(), radius: 2.0, density: 1.0, velocity: 0.0, last_update: Duration::default(), } } } impl EntityComponent for CelestialObjectSettings { fn name(&self) -> &str { Self::debug_name() } } impl ComponentDebug for CelestialObjectSettings { fn debug_name() -> &'static str { "CelestialObjectSettings" } } #[derive(Debug)] pub struct CelestialReference { pub entity: Entity, } impl EntityComponent for CelestialReference { fn name(&self) -> &str { Self::debug_name() } } impl ComponentDebug for CelestialReference { fn debug_name() -> &'static str { "CelestialReference" } }