2023-12-25 10:30:27 -08:00
|
|
|
use anyhow::{bail, Context, Result};
|
2024-01-08 21:02:02 -08:00
|
|
|
use cgmath::{Deg, Point3, Rad};
|
2023-12-25 10:30:27 -08:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2023-12-24 23:03:00 -08:00
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
use crate::{
|
|
|
|
handle::SpriteHandle, util::Polar, Content, ContentBuildContext, SystemHandle,
|
|
|
|
SystemObjectHandle,
|
|
|
|
};
|
2023-12-24 23:03:00 -08:00
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
pub(crate) mod syntax {
|
2023-12-24 23:03:00 -08:00
|
|
|
use serde::Deserialize;
|
2023-12-31 18:48:35 -08:00
|
|
|
use std::collections::HashMap;
|
2023-12-27 19:51:58 -08:00
|
|
|
// Raw serde syntax structs.
|
|
|
|
// These are never seen by code outside this crate.
|
2023-12-24 23:03:00 -08:00
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct System {
|
2023-12-27 19:51:58 -08:00
|
|
|
pub object: HashMap<String, Object>,
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Object {
|
2024-01-04 17:17:55 -08:00
|
|
|
pub sprite: String,
|
2023-12-25 10:30:27 -08:00
|
|
|
pub position: Position,
|
2023-12-24 23:03:00 -08:00
|
|
|
|
2023-12-25 16:22:44 -08:00
|
|
|
pub size: f32,
|
2023-12-25 09:01:12 -08:00
|
|
|
|
2023-12-25 16:22:44 -08:00
|
|
|
pub radius: Option<f32>,
|
|
|
|
pub angle: Option<f32>,
|
2023-12-25 10:30:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum Position {
|
|
|
|
Polar(PolarCoords),
|
2023-12-25 15:56:27 -08:00
|
|
|
Cartesian(CoordinatesThree),
|
2023-12-25 10:30:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct PolarCoords {
|
2023-12-25 15:56:27 -08:00
|
|
|
pub center: CoordinatesTwo,
|
2023-12-25 16:22:44 -08:00
|
|
|
pub radius: f32,
|
|
|
|
pub angle: f32,
|
|
|
|
pub z: f32,
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
2023-12-25 15:56:27 -08:00
|
|
|
pub enum CoordinatesTwo {
|
2023-12-24 23:03:00 -08:00
|
|
|
Label(String),
|
2023-12-25 16:22:44 -08:00
|
|
|
Coords([f32; 2]),
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
2023-12-25 09:01:12 -08:00
|
|
|
|
2023-12-25 15:56:27 -08:00
|
|
|
impl ToString for CoordinatesTwo {
|
2023-12-25 09:01:12 -08:00
|
|
|
fn to_string(&self) -> String {
|
|
|
|
match self {
|
|
|
|
Self::Label(s) => s.to_owned(),
|
|
|
|
Self::Coords(v) => format!("{:?}", v),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-12-25 15:56:27 -08:00
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum CoordinatesThree {
|
|
|
|
Label(String),
|
2023-12-25 16:22:44 -08:00
|
|
|
Coords([f32; 3]),
|
2023-12-25 15:56:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ToString for CoordinatesThree {
|
|
|
|
fn to_string(&self) -> String {
|
|
|
|
match self {
|
|
|
|
Self::Label(s) => s.to_owned(),
|
|
|
|
Self::Coords(v) => format!("{:?}", v),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
// Processed data structs.
|
|
|
|
// These are exported.
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Represents a star system
|
2024-01-11 20:21:07 -08:00
|
|
|
#[derive(Debug, Clone)]
|
2023-12-24 23:03:00 -08:00
|
|
|
pub struct System {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// This star system's name
|
2023-12-25 09:01:12 -08:00
|
|
|
pub name: String,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
/// This star system's handle
|
|
|
|
pub handle: SystemHandle,
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Objects in this system
|
2024-01-12 14:34:31 -08:00
|
|
|
pub objects: Vec<SystemObject>,
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Represents an orbiting body in a star system
|
|
|
|
/// (A star, planet, moon, satellite, etc)
|
|
|
|
/// These may be landable and may be decorative.
|
|
|
|
/// System objects to not interact with the physics engine.
|
2024-01-11 20:21:07 -08:00
|
|
|
#[derive(Debug, Clone)]
|
2024-01-12 14:34:31 -08:00
|
|
|
pub struct SystemObject {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// This object's sprite
|
2024-01-04 17:17:55 -08:00
|
|
|
pub sprite: SpriteHandle,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
/// This object's handle
|
|
|
|
pub handle: SystemObjectHandle,
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// This object's size.
|
|
|
|
/// Measured as height in game units.
|
|
|
|
/// This value is scaled for distance
|
|
|
|
/// (i.e, the z-value of position)
|
2023-12-25 16:22:44 -08:00
|
|
|
pub size: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// This object's position, in game coordinates,
|
|
|
|
/// relative to the system's center (0, 0).
|
2024-01-09 21:14:57 -08:00
|
|
|
pub pos: Point3<f32>,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
2024-01-08 21:02:02 -08:00
|
|
|
/// This object's sprite's angle.
|
|
|
|
pub angle: Rad<f32>,
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
/// Helper function for resolve_position, never called on its own.
|
2023-12-25 10:30:27 -08:00
|
|
|
fn resolve_coordinates(
|
2023-12-27 19:51:58 -08:00
|
|
|
objects: &HashMap<String, syntax::Object>,
|
|
|
|
cor: &syntax::CoordinatesThree,
|
2023-12-25 10:30:27 -08:00
|
|
|
mut cycle_detector: HashSet<String>,
|
2023-12-25 15:56:27 -08:00
|
|
|
) -> Result<Point3<f32>> {
|
2023-12-25 10:30:27 -08:00
|
|
|
match cor {
|
2023-12-27 19:51:58 -08:00
|
|
|
syntax::CoordinatesThree::Coords(c) => Ok((*c).into()),
|
|
|
|
syntax::CoordinatesThree::Label(l) => {
|
2023-12-25 10:30:27 -08:00
|
|
|
if cycle_detector.contains(l) {
|
|
|
|
bail!(
|
|
|
|
"Found coordinate cycle: `{}`",
|
|
|
|
cycle_detector.iter().fold(String::new(), |sum, a| {
|
|
|
|
if sum.is_empty() {
|
|
|
|
a.to_string()
|
|
|
|
} else {
|
|
|
|
sum + " -> " + a
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
2023-12-25 09:01:12 -08:00
|
|
|
}
|
2023-12-25 10:30:27 -08:00
|
|
|
cycle_detector.insert(l.to_owned());
|
|
|
|
|
|
|
|
let p = match objects.get(l) {
|
|
|
|
Some(p) => p,
|
|
|
|
None => bail!("Could not resolve coordinate label `{l}`"),
|
|
|
|
};
|
|
|
|
Ok(resolve_position(&objects, &p, cycle_detector)
|
|
|
|
.with_context(|| format!("In object {:#?}", l))?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
/// Given an object, resolve its position as a Point3.
|
2023-12-25 10:30:27 -08:00
|
|
|
fn resolve_position(
|
2023-12-27 19:51:58 -08:00
|
|
|
objects: &HashMap<String, syntax::Object>,
|
|
|
|
obj: &syntax::Object,
|
2023-12-25 10:30:27 -08:00
|
|
|
cycle_detector: HashSet<String>,
|
2023-12-25 15:56:27 -08:00
|
|
|
) -> Result<Point3<f32>> {
|
2023-12-25 10:30:27 -08:00
|
|
|
match &obj.position {
|
2023-12-27 19:51:58 -08:00
|
|
|
syntax::Position::Cartesian(c) => Ok(resolve_coordinates(objects, &c, cycle_detector)?),
|
|
|
|
syntax::Position::Polar(p) => {
|
2023-12-25 16:15:09 -08:00
|
|
|
let three = match &p.center {
|
2023-12-27 19:51:58 -08:00
|
|
|
syntax::CoordinatesTwo::Label(s) => syntax::CoordinatesThree::Label(s.clone()),
|
|
|
|
syntax::CoordinatesTwo::Coords(v) => {
|
|
|
|
syntax::CoordinatesThree::Coords([v[0], v[1], f32::NAN])
|
2023-12-25 16:15:09 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let r = resolve_coordinates(&objects, &three, cycle_detector)?;
|
2023-12-25 15:56:27 -08:00
|
|
|
let plane = Polar {
|
|
|
|
center: (r.x, r.y).into(),
|
|
|
|
radius: p.radius,
|
2024-01-08 21:02:02 -08:00
|
|
|
angle: Deg(p.angle).into(),
|
2023-12-25 15:56:27 -08:00
|
|
|
}
|
|
|
|
.to_cartesian();
|
|
|
|
Ok(Point3 {
|
|
|
|
x: plane.x,
|
|
|
|
y: plane.y,
|
|
|
|
z: p.z,
|
|
|
|
})
|
2023-12-25 10:30:27 -08:00
|
|
|
}
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
impl crate::Build for System {
|
2024-01-04 17:17:55 -08:00
|
|
|
type InputSyntaxType = HashMap<String, syntax::System>;
|
2023-12-30 10:58:17 -08:00
|
|
|
|
2024-01-05 12:09:59 -08:00
|
|
|
fn build(
|
|
|
|
system: Self::InputSyntaxType,
|
|
|
|
_build_context: &mut ContentBuildContext,
|
|
|
|
content: &mut Content,
|
|
|
|
) -> Result<()> {
|
2023-12-27 20:13:39 -08:00
|
|
|
for (system_name, system) in system {
|
2023-12-27 19:51:58 -08:00
|
|
|
let mut objects = Vec::new();
|
2023-12-24 23:03:00 -08:00
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
let system_handle = SystemHandle {
|
|
|
|
index: content.systems.len(),
|
|
|
|
};
|
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
for (label, obj) in &system.object {
|
|
|
|
let mut cycle_detector = HashSet::new();
|
2023-12-30 10:58:17 -08:00
|
|
|
cycle_detector.insert(label.clone());
|
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
let sprite_handle = match content.sprite_index.get(&obj.sprite) {
|
2023-12-30 10:58:17 -08:00
|
|
|
None => bail!(
|
2024-01-04 17:17:55 -08:00
|
|
|
"In system `{}`: sprite `{}` doesn't exist",
|
2023-12-30 10:58:17 -08:00
|
|
|
system_name,
|
2024-01-04 17:17:55 -08:00
|
|
|
obj.sprite
|
2023-12-30 10:58:17 -08:00
|
|
|
),
|
|
|
|
Some(t) => *t,
|
|
|
|
};
|
2023-12-27 19:51:58 -08:00
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
objects.push(SystemObject {
|
|
|
|
sprite: sprite_handle,
|
2024-01-09 21:14:57 -08:00
|
|
|
pos: resolve_position(&system.object, &obj, cycle_detector)
|
2023-12-27 19:51:58 -08:00
|
|
|
.with_context(|| format!("In object {:#?}", label))?,
|
|
|
|
size: obj.size,
|
2024-01-08 21:02:02 -08:00
|
|
|
angle: Deg(obj.angle.unwrap_or(0.0)).into(),
|
2024-01-12 14:34:31 -08:00
|
|
|
handle: SystemObjectHandle {
|
|
|
|
system_handle,
|
|
|
|
body_index: 0,
|
|
|
|
},
|
2023-12-27 19:51:58 -08:00
|
|
|
});
|
|
|
|
}
|
2023-12-24 23:03:00 -08:00
|
|
|
|
2024-01-12 14:34:31 -08:00
|
|
|
// Sort by z-distance. This is important, since these are
|
|
|
|
// rendered in this order. We need far objects to be behind
|
|
|
|
// near objects!
|
2024-01-11 22:10:36 -08:00
|
|
|
objects.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
|
2024-01-12 14:34:31 -08:00
|
|
|
|
|
|
|
// Update object handles
|
|
|
|
let mut i = 0;
|
|
|
|
for o in &mut objects {
|
|
|
|
o.handle.body_index = i;
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
2024-01-05 12:09:59 -08:00
|
|
|
content.systems.push(Self {
|
2024-01-12 14:34:31 -08:00
|
|
|
handle: system_handle,
|
2023-12-30 10:58:17 -08:00
|
|
|
name: system_name,
|
2023-12-27 19:51:58 -08:00
|
|
|
objects,
|
2023-12-24 23:03:00 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-12-30 10:58:17 -08:00
|
|
|
return Ok(());
|
2023-12-24 23:03:00 -08:00
|
|
|
}
|
|
|
|
}
|