259 lines
6.7 KiB
Rust
259 lines
6.7 KiB
Rust
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<Option<Entity>>,
|
|
) -> Result<EntityObject> {
|
|
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::<Context>();
|
|
let scene = world.resources.get::<Scene>();
|
|
|
|
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::<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 {
|
|
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<f32>, Vector3<f32>)>, 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::<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,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct CelestialObjectSettings {
|
|
pub location: Vector3<f32>,
|
|
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<f32> {
|
|
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"
|
|
}
|
|
}
|