engine/examples/free_space/src/celestial_object.rs

260 lines
6.7 KiB
Rust
Raw Normal View History

2025-03-14 12:25:11 +00:00
use core::f32;
2025-03-13 21:09:11 +00:00
use std::time::Duration;
use anyhow::Result;
use ecs::*;
use engine::prelude::{
cgmath::{Matrix4, Vector3},
*,
};
use hexasphere::shapes::IcoSphere;
2025-03-14 12:25:11 +00:00
#[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(),
}
}
}
2025-03-13 21:09:11 +00:00
pub struct CelestialObject;
impl CelestialObject {
pub fn new(
world: &mut World,
2025-03-14 12:25:11 +00:00
class: CelestialClass,
2025-03-13 21:09:11 +00:00
subdivisions: usize,
celestial_reference: impl Into<Option<Entity>>,
) -> Result<EntityObject> {
let mut entity_object = AssetHandler::create(world).empty_entity();
2025-03-14 12:25:11 +00:00
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]);
2025-03-13 21:09:11 +00:00
draw.set_transform(celestial_settings.model_matrix())?;
entity_object.insert_component(draw);
2025-03-14 12:25:11 +00:00
entity_object.insert_component(bounding_box);
2025-03-13 21:09:11 +00:00
entity_object.insert_component(celestial_settings);
if let Some(e) = celestial_reference.into() {
entity_object.insert_component(CelestialReference { entity: e });
}
Ok(entity_object)
}
2025-03-14 12:25:11 +00:00
fn create_asset(
world: &World,
class: CelestialClass,
subdivisions: usize,
) -> Result<(AssetMesh, BoundingBox)> {
2025-03-13 21:09:11 +00:00
let context = world.resources.get::<Context>();
let scene = world.resources.get::<Scene>();
let command_buffer = CommandBuffer::new_primary()
.build(context.device().clone(), context.queue().clone())?;
2025-03-14 12:25:11 +00:00
let (sphere, bounding_box) = Self::create_sphere(subdivisions);
2025-03-13 21:09:11 +00:00
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(
2025-03-14 12:25:11 +00:00
&sphere
2025-03-13 21:09:11 +00:00
.into_iter()
.map(|(v, n)| PositionNormal::new(v, n))
.collect::<Vec<_>>(),
)
.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 {
2025-03-14 12:25:11 +00:00
color: class.color(),
2025-03-13 21:09:11 +00:00
emissive_factor: [0.65, 0.65, 0.65],
metallic_factor: 0.5,
roughness_factor: 0.7,
..PrimitiveMaterial::default()
},
false,
)?;
2025-03-14 12:25:11 +00:00
Ok((asset_mesh, bounding_box))
2025-03-13 21:09:11 +00:00
}
2025-03-14 12:25:11 +00:00
fn create_sphere(subdivisions: usize) -> (Vec<(Vector3<f32>, Vector3<f32>)>, BoundingBox) {
let mut bounding_box = BoundingBox::init();
2025-03-13 21:09:11 +00:00
let sphere = IcoSphere::new(subdivisions, |_| ());
let points = sphere
.raw_points()
.into_iter()
2025-03-14 12:25:11 +00:00
.map(|v| {
let v = Vector3::from(v.to_array());
bounding_box.check(v);
2025-03-13 21:09:11 +00:00
2025-03-14 12:25:11 +00:00
v
2025-03-13 21:09:11 +00:00
})
2025-03-14 12:25:11 +00:00
.collect::<Vec<Vector3<f32>>>();
(
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,
)
2025-03-13 21:09:11 +00:00
}
}
#[derive(Debug)]
pub struct CelestialObjectSettings {
pub location: Vector3<f32>,
pub radius: f32,
pub density: f32,
pub velocity: f32,
last_update: Duration,
}
impl CelestialObjectSettings {
2025-03-14 12:25:11 +00:00
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<f32> {
Matrix4::from_translation(self.location) * Matrix4::from_scale(self.radius * Self::SCALE)
2025-03-13 21:09:11 +00:00
}
pub fn update(
&mut self,
now: Duration,
draw: &Draw,
celestial_object_settings_reference: &CelestialObjectSettings,
) -> Result<()> {
2025-03-14 12:25:11 +00:00
self.last_update = now;
// TODO: velocity calculation around celestial reference
2025-03-13 21:09:11 +00:00
Ok(())
}
}
impl Default for CelestialObjectSettings {
fn default() -> Self {
Self {
location: Vector3::zero(),
2025-03-14 12:25:11 +00:00
radius: 2.0,
2025-03-13 21:09:11 +00:00
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"
}
}