From d014e085d5e5553b1934488d08da5d3e55f766f5 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 12 Jan 2024 22:47:40 -0800 Subject: [PATCH] Removed cgmath --- Cargo.lock | 37 ++----- Cargo.toml | 2 - crates/content/Cargo.toml | 3 +- crates/content/src/part/effect.rs | 21 ++-- crates/content/src/part/outfit.rs | 12 +-- crates/content/src/part/ship.rs | 115 ++++++++++++--------- crates/content/src/part/system.rs | 19 ++-- crates/content/src/util.rs | 17 +-- crates/galactica/Cargo.toml | 2 +- crates/galactica/src/game.rs | 10 +- crates/galactica/src/main.rs | 18 ++-- crates/playeragent/Cargo.toml | 2 +- crates/playeragent/src/camera.rs | 6 +- crates/playeragent/src/playerstatus.rs | 4 +- crates/render/Cargo.toml | 2 +- crates/render/shaders/object.wgsl | 8 +- crates/render/src/datastructs.rs | 4 +- crates/render/src/gpustate/phys.rs | 65 ++++++------ crates/render/src/gpustate/render.rs | 10 +- crates/render/src/lib.rs | 3 +- crates/render/src/starfield.rs | 39 +++---- crates/render/src/ui/radar.rs | 103 +++++++++--------- crates/system/Cargo.toml | 1 - crates/system/src/data/ship/ship.rs | 54 ++++------ crates/system/src/phys/controller/point.rs | 21 ++-- crates/system/src/phys/mod.rs | 1 - crates/system/src/phys/objects/collapse.rs | 49 ++++----- crates/system/src/phys/objects/ship.rs | 32 +++--- crates/system/src/phys/particlebuilder.rs | 19 ++-- crates/system/src/phys/systemsim.rs | 89 +++++++--------- crates/system/src/phys/util.rs | 32 ------ crates/util/src/lib.rs | 5 + 32 files changed, 349 insertions(+), 456 deletions(-) delete mode 100644 crates/system/src/phys/util.rs diff --git a/Cargo.lock b/Cargo.lock index 55eec09..df9b2c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,15 +90,6 @@ version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" -[[package]] -name = "approx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" -dependencies = [ - "num-traits", -] - [[package]] name = "approx" version = "0.5.1" @@ -276,16 +267,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "cgmath" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" -dependencies = [ - "approx 0.4.0", - "num-traits", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -641,12 +622,12 @@ name = "galactica" version = "0.0.0" dependencies = [ "anyhow", - "cgmath", "galactica-content", "galactica-playeragent", "galactica-render", "galactica-system", "galactica-util", + "nalgebra", "pollster", "rand", "wgpu", @@ -658,10 +639,11 @@ name = "galactica-content" version = "0.0.0" dependencies = [ "anyhow", - "cgmath", "galactica-packer", + "galactica-util", "image", "nalgebra", + "rapier2d", "serde", "toml", "walkdir", @@ -684,9 +666,9 @@ name = "galactica-playeragent" version = "0.0.0" dependencies = [ "anyhow", - "cgmath", "galactica-content", "galactica-util", + "nalgebra", "pollster", "rapier2d", "wgpu", @@ -699,7 +681,6 @@ version = "0.0.0" dependencies = [ "anyhow", "bytemuck", - "cgmath", "galactica-content", "galactica-packer", "galactica-playeragent", @@ -707,6 +688,7 @@ dependencies = [ "galactica-util", "glyphon", "image", + "nalgebra", "rand", "wgpu", "winit", @@ -716,7 +698,6 @@ dependencies = [ name = "galactica-system" version = "0.0.0" dependencies = [ - "cgmath", "crossbeam", "galactica-content", "galactica-playeragent", @@ -1181,7 +1162,7 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" dependencies = [ - "approx 0.5.1", + "approx", "matrixmultiply", "nalgebra-macros", "num-complex", @@ -1465,7 +1446,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "104ae65232e20477a98f9f1e75ca9850eae24a2ea846a2b1a0af03ad752136ce" dependencies = [ - "approx 0.5.1", + "approx", "arrayvec", "bitflags 1.3.2", "downcast-rs", @@ -1620,7 +1601,7 @@ version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f94d294a9b96694c14888dd0e8ce77620dcc4f2f49264109ef835fa5e2285b84" dependencies = [ - "approx 0.5.1", + "approx", "arrayvec", "bit-vec", "bitflags 1.3.2", @@ -1818,7 +1799,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ - "approx 0.5.1", + "approx", "num-complex", "num-traits", "paste", diff --git a/Cargo.toml b/Cargo.toml index a263a3a..6b6047e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,8 +61,6 @@ nalgebra = "0.32.3" crossbeam = "0.8.3" pollster = "0.3" anyhow = "1.0" -# TODO: migrate to nalgebra -cgmath = "0.18.0" rand = "0.8.5" walkdir = "2.4.0" toml = "0.8.8" diff --git a/crates/content/Cargo.toml b/crates/content/Cargo.toml index 5fc5ffa..97e6de0 100644 --- a/crates/content/Cargo.toml +++ b/crates/content/Cargo.toml @@ -18,11 +18,12 @@ workspace = true [dependencies] galactica-packer = { workspace = true } +galactica-util = { workspace = true } serde = { workspace = true } toml = { workspace = true } anyhow = { workspace = true } -cgmath = { workspace = true } walkdir = { workspace = true } nalgebra = { workspace = true } image = { workspace = true } +rapier2d = { workspace = true } diff --git a/crates/content/src/part/effect.rs b/crates/content/src/part/effect.rs index 7f67d39..a3b847f 100644 --- a/crates/content/src/part/effect.rs +++ b/crates/content/src/part/effect.rs @@ -1,12 +1,11 @@ use anyhow::{Context, Result}; -use cgmath::Rad; use std::collections::HashMap; use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle}; pub(crate) mod syntax { use anyhow::{bail, Result}; - use cgmath::Deg; + use galactica_util::to_radians; use serde::Deserialize; use crate::{Content, ContentBuildContext, EffectHandle}; @@ -75,10 +74,10 @@ pub(crate) mod syntax { size_rng: self.size_rng.unwrap_or(0.0), lifetime, lifetime_rng: self.lifetime_rng.unwrap_or(0.0), - angle: Deg(self.angle.unwrap_or(0.0) / 2.0).into(), - angle_rng: self.angle_rng.unwrap_or(0.0) / 2.0, - angvel: Deg(self.angvel.unwrap_or(0.0)).into(), - angvel_rng: self.angvel_rng.unwrap_or(0.0), + angle: to_radians(self.angle.unwrap_or(0.0) / 2.0), + angle_rng: to_radians(self.angle_rng.unwrap_or(0.0) / 2.0), + angvel: to_radians(self.angvel.unwrap_or(0.0)), + angvel_rng: to_radians(self.angvel_rng.unwrap_or(0.0)), velocity_scale_parent: self.velocity_scale_parent.unwrap_or(0.0), velocity_scale_parent_rng: self.velocity_scale_parent_rng.unwrap_or(0.0), velocity_scale_target: self.velocity_scale_target.unwrap_or(0.0), @@ -141,14 +140,14 @@ pub struct Effect { /// Random lifetime variation pub lifetime_rng: f32, - /// The angle this particle points once spawned - pub angle: Rad, + /// The angle this particle points once spawned, in radians + pub angle: f32, - /// Random angle variation + /// Random angle variation, in radians pub angle_rng: f32, - /// How fast this particle spins - pub angvel: Rad, + /// How fast this particle spins, in radians/sec + pub angvel: f32, /// Random angvel variation pub angvel_rng: f32, diff --git a/crates/content/src/part/outfit.rs b/crates/content/src/part/outfit.rs index acc8806..8bd4ef6 100644 --- a/crates/content/src/part/outfit.rs +++ b/crates/content/src/part/outfit.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; - use anyhow::{bail, Context, Result}; -use cgmath::Rad; use serde::Deserialize; +use std::collections::HashMap; use crate::{ handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace, @@ -11,7 +9,7 @@ use crate::{ pub(crate) mod syntax { use crate::{effect, part::outfitspace, ContentBuildContext}; use anyhow::{bail, Result}; - use cgmath::Deg; + use galactica_util::to_radians; use serde::Deserialize; // Raw serde syntax structs. // These are never seen by code outside this crate. @@ -91,7 +89,7 @@ pub(crate) mod syntax { // Divide by 2, so the angle matches the angle of the fire cone. // This should ALWAYS be done in the content parser. - angle_rng: Deg(self.projectile.angle_rng / 2.0).into(), + angle_rng: to_radians(self.projectile.angle_rng / 2.0).into(), impact_effect, expire_effect, collider: self.projectile.collider, @@ -214,8 +212,8 @@ pub struct Projectile { /// The force this projectile applies pub force: f32, - /// The angle variation of this projectile. - pub angle_rng: Rad, + /// The angle variation of this projectile, in radians + pub angle_rng: f32, /// The particle this projectile will spawn when it hits something pub impact_effect: Option, diff --git a/crates/content/src/part/ship.rs b/crates/content/src/part/ship.rs index 4458146..061c4e2 100644 --- a/crates/content/src/part/ship.rs +++ b/crates/content/src/part/ship.rs @@ -1,8 +1,8 @@ -use std::{collections::HashMap, hash::Hash}; - use anyhow::{bail, Context, Result}; -use cgmath::Point2; -use nalgebra::{point, Point}; +use galactica_util::to_radians; +use nalgebra::{Point2, Rotation2, Vector2}; +use rapier2d::geometry::{Collider, ColliderBuilder}; +use std::{collections::HashMap, fmt::Debug, hash::Hash}; use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitSpace}; @@ -116,7 +116,7 @@ pub struct Ship { pub hull: f32, /// Collision shape for this ship - pub collision: Collision, + pub collider: CollisionDebugWrapper, /// Remove later pub aspect: f32, @@ -137,11 +137,15 @@ pub struct Ship { pub damage: ShipDamage, } -/// Collision shape for this ship -#[derive(Debug, Clone)] -pub struct Collision { - pub points: Vec>, - pub indices: Vec<[u32; 2]>, +/// Hack to give `Collider` a fake debug method. +/// Pretend this is transparent, get the collider with .0. +#[derive(Clone)] +pub struct CollisionDebugWrapper(pub Collider); + +impl Debug for CollisionDebugWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + "CollisionDebugWrapper".fmt(f) + } } /// An engine point on a ship. @@ -150,7 +154,7 @@ pub struct Collision { pub struct EnginePoint { /// This engine point's position, in game units, /// relative to the ship's center. - pub pos: Point2, + pub pos: Vector2, /// The size of the flare that should be drawn /// at this point, measured as height in game units. @@ -165,7 +169,7 @@ pub struct GunPoint { /// This gun point's position, in game units, /// relative to the ship's center. - pub pos: Point2, + pub pos: Vector2, } impl Eq for GunPoint {} @@ -281,9 +285,8 @@ impl crate::Build for Ship { .to_handle(build_context, ct) .with_context(|| format!("while loading ship `{}`", ship_name))?, count: e.count, - pos: e.pos.map(|p| Point2 { - x: p[0] * (size / 2.0) * aspect, - y: p[1] * size / 2.0, + pos: e.pos.map(|p| { + Point2::new(p[0] * (size / 2.0) * aspect, p[1] * size / 2.0) }), }); } @@ -302,9 +305,11 @@ impl crate::Build for Ship { format!("while loading ship `{}`", ship_name) })?, count: g.count, - pos: g.pos.map(|p| Point2 { - x: p[0] * (size / 2.0) * aspect, - y: p[1] * size / 2.0, + pos: g.pos.map(|p| { + Point2::new( + p[0] * (size / 2.0) * aspect, + p[1] * size / 2.0, + ) }), }) } @@ -342,9 +347,8 @@ impl crate::Build for Ship { .to_handle(build_context, ct) .with_context(|| format!("while loading ship `{}`", ship_name))?, frequency: e.frequency, - pos: e.pos.map(|p| Point2 { - x: p[0] * (size / 2.0) * aspect, - y: p[1] * size / 2.0, + pos: e.pos.map(|p| { + Point2::new(p[0] * (size / 2.0) * aspect, p[1] * size / 2.0) }), }); } @@ -367,13 +371,49 @@ impl crate::Build for Ship { for g in ship.guns { guns.push(GunPoint { idx: guns.len() as u32, - pos: Point2 { - x: g.x * size * aspect / 2.0, - y: g.y * size / 2.0, - }, + + // Angle adjustment, since sprites point north + // and 0 degrees is east in the game + pos: Rotation2::new(to_radians(-90.0)) + * Vector2::new(g.x * size * aspect / 2.0, g.y * size / 2.0), }) } + // Build rapier2d collider + let collider = { + let indices: Vec<[u32; 2]> = (0..ship.collision.len()) + .map(|x| { + // Auto-generate mesh lines: + // [ [0, 1], [1, 2], ..., [n, 0] ] + let next = if x == ship.collision.len() - 1 { + 0 + } else { + x + 1 + }; + [x as u32, next as u32] + }) + .collect(); + + let points: Vec> = ship + .collision + .iter() + .map(|x| { + // Angle adjustment: rotate collider to match sprite + // (Sprites (and their colliders) point north, but 0 is east in the game world) + // We apply this pointwise so that local points inside the collider work as we expect. + // + // If we don't, rapier2 will compute local points pre-rotation, + // which will break particle placement on top of ships (i.e, collapse effects) + Rotation2::new(to_radians(-90.0)) + * Point2::new(x[0] * (size / 2.0) * aspect, x[1] * size / 2.0) + }) + .collect(); + + ColliderBuilder::convex_decomposition(&points[..], &indices[..]) + .mass(ship.mass) + .build() + }; + ct.ships.push(Self { aspect, collapse, @@ -391,35 +431,14 @@ impl crate::Build for Ship { .engines .iter() .map(|e| EnginePoint { - pos: Point2 { - x: e.x * size * aspect / 2.0, - y: e.y * size / 2.0, - }, + pos: Vector2::new(e.x * size * aspect / 2.0, e.y * size / 2.0), size: e.size, }) .collect(), guns, - collision: Collision { - indices: (0..ship.collision.len()) - .map(|x| { - // Auto-generate mesh lines: - // [ [0, 1], [1, 2], ..., [n, 0] ] - let next = if x == ship.collision.len() - 1 { - 0 - } else { - x + 1 - }; - [x as u32, next as u32] - }) - .collect(), - points: ship - .collision - .iter() - .map(|x| point![x[0] * (size / 2.0) * aspect, x[1] * size / 2.0]) - .collect(), - }, + collider: CollisionDebugWrapper(collider), }); } diff --git a/crates/content/src/part/system.rs b/crates/content/src/part/system.rs index 2fa9981..887cf00 100644 --- a/crates/content/src/part/system.rs +++ b/crates/content/src/part/system.rs @@ -1,5 +1,6 @@ use anyhow::{bail, Context, Result}; -use cgmath::{Deg, Point3, Rad}; +use galactica_util::to_radians; +use nalgebra::{Point2, Point3}; use std::collections::{HashMap, HashSet}; use crate::{ @@ -115,8 +116,8 @@ pub struct SystemObject { /// relative to the system's center (0, 0). pub pos: Point3, - /// This object's sprite's angle. - pub angle: Rad, + /// This object's sprite's angle, in radians + pub angle: f32, } /// Helper function for resolve_position, never called on its own. @@ -169,16 +170,12 @@ fn resolve_position( }; let r = resolve_coordinates(&objects, &three, cycle_detector)?; let plane = Polar { - center: (r.x, r.y).into(), + center: Point2::new(r.x, r.y), radius: p.radius, - angle: Deg(p.angle).into(), + angle: to_radians(p.angle), } .to_cartesian(); - Ok(Point3 { - x: plane.x, - y: plane.y, - z: p.z, - }) + Ok(Point3::new(plane.x, plane.y, p.z)) } } } @@ -216,7 +213,7 @@ impl crate::Build for System { pos: resolve_position(&system.object, &obj, cycle_detector) .with_context(|| format!("In object {:#?}", label))?, size: obj.size, - angle: Deg(obj.angle.unwrap_or(0.0)).into(), + angle: to_radians(obj.angle.unwrap_or(0.0)), handle: SystemObjectHandle { system_handle, body_index: 0, diff --git a/crates/content/src/util.rs b/crates/content/src/util.rs index bbeeb50..073d9f5 100644 --- a/crates/content/src/util.rs +++ b/crates/content/src/util.rs @@ -1,18 +1,23 @@ -use cgmath::{Angle, Point2, Rad, Vector2}; +use nalgebra::{Point2, Vector2}; #[derive(Debug, Clone, Copy)] pub struct Polar { + /// The center of this polar coordinate pub center: Point2, + + /// The radius of this polar coordinate pub radius: f32, - pub angle: Rad, + + /// In radians + pub angle: f32, } impl Polar { pub fn to_cartesian(self) -> Point2 { - let v = Vector2 { - x: self.radius * self.angle.sin(), - y: self.radius * self.angle.cos(), - }; + let v = Vector2::new( + self.radius * self.angle.sin(), + self.radius * self.angle.cos(), + ); return self.center + v; } diff --git a/crates/galactica/Cargo.toml b/crates/galactica/Cargo.toml index 48b93fa..dddaaa3 100644 --- a/crates/galactica/Cargo.toml +++ b/crates/galactica/Cargo.toml @@ -32,4 +32,4 @@ winit = { workspace = true } wgpu = { workspace = true } pollster = { workspace = true } anyhow = { workspace = true } -cgmath = { workspace = true } +nalgebra = { workspace = true } diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index b035044..f367455 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -1,4 +1,3 @@ -use cgmath::Point2; use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle}; use galactica_playeragent::PlayerAgent; use galactica_system::data::ShipPersonality; @@ -6,6 +5,7 @@ use galactica_system::phys::{ ParticleBuilder, PhysSim, PhysSimShipHandle, PhysStepResources, Wrapper, }; use galactica_util::timing::Timing; +use nalgebra::Point2; use rand::seq::SliceRandom; use std::time::Instant; @@ -44,7 +44,7 @@ impl<'a> Game { ShipHandle { index: 0 }, FactionHandle { index: 0 }, ShipPersonality::Player, - Point2 { x: 0.0, y: 0.0 }, + Point2::new(0.0, 0.0), ); let s = self.state.systemsim.get_ship_mut(&player).unwrap(); @@ -66,7 +66,7 @@ impl<'a> Game { ShipHandle { index: 0 }, FactionHandle { index: 1 }, ShipPersonality::Point, - Point2 { x: 100.0, y: 0.0 }, + Point2::new(100.0, 0.0), ); let s = systemsim.get_ship_mut(&a).unwrap(); @@ -78,8 +78,8 @@ impl<'a> Game { &ct, ShipHandle { index: 0 }, FactionHandle { index: 0 }, - ShipPersonality::Point, - Point2 { x: 0.0, y: 120.0 }, + ShipPersonality::Dummy, + Point2::new(0.0, 120.0), ); let s = systemsim.get_ship_mut(&a).unwrap(); diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index e06880b..c0a4526 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -1,15 +1,12 @@ mod game; use anyhow::{bail, Result}; -use cgmath::Point2; use galactica_content::{Content, SystemHandle}; use galactica_playeragent::{PlayerAgent, PlayerStatus}; use galactica_render::RenderInput; -use galactica_system::{ - data::ShipState, - phys::{util, PhysSimShipHandle}, -}; +use galactica_system::{data::ShipState, phys::PhysSimShipHandle}; use galactica_util::constants::ASSET_CACHE; +use nalgebra::Vector2; use std::{ fs, path::{Path, PathBuf}, @@ -92,7 +89,7 @@ fn main() -> Result<()> { let r = &game.get_state().systemsim.get_rigid_body(o.rigid_body); if let Some(r) = r { - Some(util::rigidbody_position(r)) + Some(*r.translation()) } else { None } @@ -100,19 +97,16 @@ fn main() -> Result<()> { ShipState::UnLanding { .. } => { let pos = o.data.get_state().unlanding_position(&content).unwrap(); - Some(Point2 { x: pos.x, y: pos.y }) + Some(Vector2::new(pos.x, pos.y)) } ShipState::Landing { .. } => { let pos = o.data.get_state().landing_position(&content).unwrap(); - Some(Point2 { x: pos.x, y: pos.y }) + Some(Vector2::new(pos.x, pos.y)) } ShipState::Landed { target } => { let b = content.get_system_object(*target); - Some(Point2 { - x: b.pos.x, - y: b.pos.y, - }) + Some(Vector2::new(b.pos.x, b.pos.y)) } ShipState::Dead => None, diff --git a/crates/playeragent/Cargo.toml b/crates/playeragent/Cargo.toml index 70cf870..698d34d 100644 --- a/crates/playeragent/Cargo.toml +++ b/crates/playeragent/Cargo.toml @@ -24,5 +24,5 @@ winit = { workspace = true } wgpu = { workspace = true } pollster = { workspace = true } anyhow = { workspace = true } -cgmath = { workspace = true } +nalgebra = { workspace = true } rapier2d = { workspace = true } diff --git a/crates/playeragent/src/camera.rs b/crates/playeragent/src/camera.rs index f1c2dd7..1545792 100644 --- a/crates/playeragent/src/camera.rs +++ b/crates/playeragent/src/camera.rs @@ -1,9 +1,9 @@ -use cgmath::Point2; +use nalgebra::Vector2; #[derive(Debug, Clone, Copy)] pub struct Camera { /// Camera center - pub pos: Point2, + pub pos: Vector2, /// Camera zoom /// (How many game units tall is the viewport?) @@ -16,7 +16,7 @@ pub struct Camera { impl Camera { pub fn new() -> Self { Self { - pos: (0.0, 0.0).into(), + pos: Vector2::new(0.0, 0.0), zoom: 500.0, aspect: 1.0, } diff --git a/crates/playeragent/src/playerstatus.rs b/crates/playeragent/src/playerstatus.rs index 3389125..64b0a25 100644 --- a/crates/playeragent/src/playerstatus.rs +++ b/crates/playeragent/src/playerstatus.rs @@ -1,5 +1,5 @@ -use cgmath::Point2; +use nalgebra::Vector2; pub struct PlayerStatus { - pub pos: Option>, + pub pos: Option>, } diff --git a/crates/render/Cargo.toml b/crates/render/Cargo.toml index 7a824e9..8a443f6 100644 --- a/crates/render/Cargo.toml +++ b/crates/render/Cargo.toml @@ -24,7 +24,7 @@ galactica-system = { workspace = true } galactica-playeragent = { workspace = true } anyhow = { workspace = true } -cgmath = { workspace = true } +nalgebra = { workspace = true } rand = { workspace = true } image = { workspace = true } winit = { workspace = true } diff --git a/crates/render/shaders/object.wgsl b/crates/render/shaders/object.wgsl index b489d77..8d4f4eb 100644 --- a/crates/render/shaders/object.wgsl +++ b/crates/render/shaders/object.wgsl @@ -41,8 +41,8 @@ fn transform_vertex(obj: ObjectData, vertex_position: vec2, sprite_index: u // Apply rotation pos = mat2x2( - vec2(cos(obj.angle), sin(obj.angle)), - vec2(-sin(obj.angle), cos(obj.angle)) + vec2(cos(obj.angle - 1.5708), sin(obj.angle - 1.5708)), + vec2(-sin(obj.angle - 1.5708), cos(obj.angle - 1.5708)) ) * pos; @@ -81,8 +81,8 @@ fn transform_vertex(obj: ObjectData, vertex_position: vec2, sprite_index: u // Apply parent's rotation pos = mat2x2( - vec2(cos(parent.angle), sin(parent.angle)), - vec2(-sin(parent.angle), cos(parent.angle)) + vec2(cos(parent.angle - 1.5708), sin(parent.angle - 1.5708)), + vec2(-sin(parent.angle - 1.5708), cos(parent.angle - 1.5708)) ) * pos; // Correct for screen aspect, preserving height diff --git a/crates/render/src/datastructs.rs b/crates/render/src/datastructs.rs index 7526587..8318d1e 100644 --- a/crates/render/src/datastructs.rs +++ b/crates/render/src/datastructs.rs @@ -1,9 +1,9 @@ -use cgmath::Point2; use galactica_content::{Content, SystemHandle}; use galactica_playeragent::PlayerAgent; use galactica_system::phys::{ParticleBuilder, PhysSim}; use galactica_util::timing::Timing; use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; +use nalgebra::Vector2; use std::rc::Rc; use wgpu::BufferAddress; use winit::window::Window; @@ -13,7 +13,7 @@ use crate::{globaluniform::GlobalUniform, vertexbuffer::VertexBuffer}; /// Bundles parameters passed to a single call to GPUState::render pub struct RenderInput<'a> { /// Camera position, in world units - pub camera_pos: Point2, + pub camera_pos: Vector2, /// Player ship data pub player: &'a PlayerAgent, diff --git a/crates/render/src/gpustate/phys.rs b/crates/render/src/gpustate/phys.rs index 90d2151..ba8137c 100644 --- a/crates/render/src/gpustate/phys.rs +++ b/crates/render/src/gpustate/phys.rs @@ -1,9 +1,9 @@ //! GPUState routines for drawing items in a systemsim use bytemuck; -use cgmath::{EuclideanSpace, InnerSpace, Point2, Point3, Rad, Vector2}; -use galactica_system::{data::ShipState, phys::util}; -use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT; +use galactica_system::data::ShipState; +use galactica_util::{constants::OBJECT_SPRITE_INSTANCE_LIMIT, to_radians}; +use nalgebra::{Point2, Point3, Vector2}; use crate::{ globaluniform::ObjectData, @@ -29,14 +29,10 @@ impl GPUState { ShipState::Collapsing { .. } | ShipState::Flying => { let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap(); - let pos = util::rigidbody_position(&r); - ship_pos = Point3 { - x: pos.x, - y: pos.y, - z: 1.0, - }; - let ship_rot = util::rigidbody_rotation(r); - ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix! + let pos = *r.translation(); + ship_pos = Point3::new(pos.x, pos.y, 1.0); + let ship_rot = r.rotation(); + ship_ang = ship_rot.angle(); ship_cnt = state.ct.get_ship(ship.data.get_content()); } ShipState::Landing { @@ -48,14 +44,11 @@ impl GPUState { } => { let target = state.ct.get_system_object(*target); - let diff = Point2 { - x: target.pos.x, - y: target.pos.y, - } - from_position; + let diff = Point2::new(target.pos.x, target.pos.y) - from_position; ship_pos = ship.data.get_state().landing_position(state.ct).unwrap(); - let target_angle = -diff.angle(Vector2 { x: 0.0, y: 1.0 }); + let target_angle = diff.angle(&Vector2::new(1.0, 0.0)); ship_ang = from_angle + ((target_angle - from_angle) * 1f32.min(elapsed / 1.0)); ship_cnt = state.ct.get_ship(ship.data.get_content()); @@ -65,7 +58,7 @@ impl GPUState { to_angle, elapsed, .. } => { ship_pos = ship.data.get_state().unlanding_position(state.ct).unwrap(); - ship_ang = Rad(0.0) + ((to_angle - Rad(0.0)) * 1f32.min(elapsed / 1.0)); + ship_ang = 0.0 + ((to_angle - 0.0) * 1f32.min(elapsed / 1.0)); ship_cnt = state.ct.get_ship(ship.data.get_content()); } } @@ -73,11 +66,8 @@ impl GPUState { // Position adjusted for parallax // TODO: adjust parallax for zoom? // 1.0 is z-coordinate, which is constant for ships - let pos: Point2 = (Point2 { - x: ship_pos.x, - y: ship_pos.y, - } - state.camera_pos.to_vec()) - / ship_pos.z; + let pos: Point2 = + (Point2::new(ship_pos.x, ship_pos.y) - state.camera_pos) / ship_pos.z; // Game dimensions of this sprite post-scale. // Post-scale width or height, whichever is larger. @@ -104,7 +94,7 @@ impl GPUState { xpos: ship_pos.x, ypos: ship_pos.y, zpos: ship_pos.z, - angle: ship_ang.0, + angle: ship_ang, size: ship_cnt.size, parent: 0, is_child: 0, @@ -142,10 +132,17 @@ impl GPUState { &self.state.global_uniform.object_buffer, ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64, bytemuck::cast_slice(&[ObjectData { + // Note that we adjust the y-coordinate for half-height, + // not the x-coordinate, even though our ships point east + // at 0 degrees. This is because this is placed pre-rotation, + // and the parent rotation adjustment in our object shader + // automatically accounts for this. xpos: engine_point.pos.x, ypos: engine_point.pos.y - engine_point.size / 2.0, zpos: 1.0, - angle: 0.0, + // We still need an adjustment here, though, + // since engine sprites point north (with exhaust towards the south) + angle: to_radians(90.0), size: engine_point.size, parent: idx as u32, is_child: 1, @@ -183,15 +180,15 @@ impl GPUState { ) { for p in state.systemsim.iter_projectiles() { let r = state.systemsim.get_rigid_body(p.rigid_body).unwrap(); - let proj_pos = util::rigidbody_position(&r); - let proj_rot = util::rigidbody_rotation(r); - let proj_ang = -proj_rot.angle(Vector2 { x: 1.0, y: 0.0 }); + let proj_pos = *r.translation(); + let proj_rot = r.rotation(); + let proj_ang = proj_rot.angle(); let proj_cnt = &p.content; // TODO: don't clone this? // Position adjusted for parallax // TODO: adjust parallax for zoom? - // 1.0 is z-coordinate, which is constant for ships - let pos: Point2 = (proj_pos - state.camera_pos.to_vec()) / 1.0; + // 1.0 is z-coordinate, which is constant for projectiles + let pos = (proj_pos - state.camera_pos) / 1.0; // Game dimensions of this sprite post-scale. // Post-scale width or height, whichever is larger. @@ -218,7 +215,7 @@ impl GPUState { xpos: proj_pos.x, ypos: proj_pos.y, zpos: 1.0, - angle: proj_ang.0, + angle: proj_ang, size: 0f32.max(proj_cnt.size + p.size_rng), parent: 0, is_child: 0, @@ -255,11 +252,7 @@ impl GPUState { for o in &system.objects { // Position adjusted for parallax - let pos: Point2 = (Point2 { - x: o.pos.x, - y: o.pos.y, - } - state.camera_pos.to_vec()) - / o.pos.z; + let pos: Point2 = (Point2::new(o.pos.x, o.pos.y) - state.camera_pos) / o.pos.z; // Game dimensions of this sprite post-scale. // Post-scale width or height, whichever is larger. @@ -286,7 +279,7 @@ impl GPUState { xpos: o.pos.x, ypos: o.pos.y, zpos: o.pos.z, - angle: o.angle.0, + angle: o.angle, size: o.size, parent: 0, is_child: 0, diff --git a/crates/render/src/gpustate/render.rs b/crates/render/src/gpustate/render.rs index e71dbc7..e3d0c41 100644 --- a/crates/render/src/gpustate/render.rs +++ b/crates/render/src/gpustate/render.rs @@ -1,8 +1,8 @@ use anyhow::Result; use bytemuck; -use cgmath::Point2; use galactica_util::constants::PARTICLE_SPRITE_INSTANCE_LIMIT; use glyphon::Resolution; +use nalgebra::Point2; use std::iter; use wgpu; @@ -87,8 +87,8 @@ impl super::GPUState { bytemuck::cast_slice(&[ParticleInstance { position: [i.pos.x, i.pos.y], velocity: i.velocity.into(), - angle: i.angle.0, - angvel: i.angvel.0, + angle: i.angle, + angvel: i.angvel, size: i.size, sprite_index: i.sprite.get_index(), created: input.current_time, @@ -106,8 +106,8 @@ impl super::GPUState { // Game coordinates (relative to camera) of ne and sw corners of screen. // Used to skip off-screen sprites. - let clip_ne = Point2::from((-self.state.window_aspect, 1.0)) * input.camera_zoom; - let clip_sw = Point2::from((self.state.window_aspect, -1.0)) * input.camera_zoom; + let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom; + let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom; // TODO: sorting. We don't need to sort ships, but we do need to sort system objects by z-level // (which we don't yet draw) diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index 5d0cb4d..9cfe681 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -21,8 +21,7 @@ mod vertexbuffer; pub use anchoredposition::PositionAnchor; pub use datastructs::RenderInput; pub use gpustate::GPUState; - -use cgmath::Matrix4; +use nalgebra::Matrix4; /// Shader entry points pub(crate) const SHADER_MAIN_VERTEX: &'static str = "vertex_main"; diff --git a/crates/render/src/starfield.rs b/crates/render/src/starfield.rs index a5178e9..a8f1842 100644 --- a/crates/render/src/starfield.rs +++ b/crates/render/src/starfield.rs @@ -1,5 +1,5 @@ -use cgmath::{Point2, Point3, Vector2, Vector3}; use galactica_content::Content; +use nalgebra::{Point2, Point3, Vector2, Vector3}; use rand::{self, Rng}; use crate::{ @@ -40,20 +40,17 @@ impl Starfield { let sz = ct.get_config().starfield_size as f32 / 2.0; self.stars = (0..ct.get_config().starfield_count) .map(|_| StarfieldStar { - pos: Point3 { - x: rng.gen_range(-sz..=sz), - y: rng.gen_range(-sz..=sz), - z: rng.gen_range( + pos: Point3::new( + rng.gen_range(-sz..=sz), + rng.gen_range(-sz..=sz), + rng.gen_range( ct.get_config().starfield_min_dist..=ct.get_config().starfield_max_dist, ), - }, + ), size: rng.gen_range( ct.get_config().starfield_min_size..ct.get_config().starfield_max_size, ), - tint: Vector2 { - x: rng.gen_range(0.0..=1.0), - y: rng.gen_range(0.0..=1.0), - }, + tint: Vector2::new(rng.gen_range(0.0..=1.0), rng.gen_range(0.0..=1.0)), }) .collect(); } @@ -62,52 +59,48 @@ impl Starfield { let sz = ct.get_config().starfield_size as f32; // Compute window size in starfield tiles - let mut nw_tile: Point2 = { + let mut nw_tile = { // Game coordinates (relative to camera) of nw corner of screen. - let clip_nw = Point2::from((state.window_aspect, 1.0)) * ct.get_config().zoom_max; + let clip_nw = Point2::new(state.window_aspect, 1.0) * ct.get_config().zoom_max; // Parallax correction. // Also, adjust v for mod to work properly // (v is centered at 0) let v: Point2 = clip_nw * ct.get_config().starfield_min_dist; - let v_adj: Point2 = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into(); + let v_adj = Point2::new(v.x + (sz / 2.0), v.y + (sz / 2.0)); #[rustfmt::skip] // Compute m = fmod(x, sz) - let m: Vector2 = ( + let m = Vector2::new( (v_adj.x - (v_adj.x / sz).floor() * sz) - (sz / 2.0), (v_adj.y - (v_adj.y / sz).floor() * sz) - (sz / 2.0) - ).into(); + ); // Now, remainder and convert to "relative tile" coordinates // ( where (0,0) is center tile, (0, 1) is north, etc) let rel = (v - m) / sz; // relative coordinates of north-east tile - (rel.x.round() as i32, rel.y.round() as i32).into() + Point2::new(rel.x.round() as i32, rel.y.round() as i32) }; // We need to cover the window with stars, // but we also need a one-wide buffer to account for motion. - nw_tile += Vector2::from((1, 1)); + nw_tile += Vector2::new(1, 1); // Truncate tile grid to buffer size // (The window won't be full of stars if our instance limit is too small) while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * ct.get_config().starfield_count as i32) > ct.get_config().starfield_instance_limit as i32 { - nw_tile -= Vector2::from((1, 1)); + nw_tile -= Vector2::new(1, 1); } // Add all tiles to buffer self.instance_count = 0; // Keep track of buffer index for x in (-nw_tile.x)..=nw_tile.x { for y in (-nw_tile.y)..=nw_tile.y { - let offset = Vector3 { - x: sz * x as f32, - y: sz * y as f32, - z: 0.0, - }; + let offset = Vector3::new(sz * x as f32, sz * y as f32, 0.0); for s in &self.stars { state.queue.write_buffer( &state.vertex_buffers.starfield.instances, diff --git a/crates/render/src/ui/radar.rs b/crates/render/src/ui/radar.rs index a14ec63..e606fb4 100644 --- a/crates/render/src/ui/radar.rs +++ b/crates/render/src/ui/radar.rs @@ -1,6 +1,6 @@ -use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2}; -use galactica_system::{data::ShipState, phys::util}; -use galactica_util::constants::UI_SPRITE_INSTANCE_LIMIT; +use galactica_system::data::ShipState; +use galactica_util::{constants::UI_SPRITE_INSTANCE_LIMIT, to_radians}; +use nalgebra::{Point2, Vector2}; use crate::{ datastructs::RenderState, @@ -15,7 +15,7 @@ pub(super) struct Radar { impl Radar { pub fn new() -> Self { Self { - last_player_position: Point2 { x: 0.0, y: 0.0 }, + last_player_position: Point2::new(0.0, 0.0), } } } @@ -47,7 +47,7 @@ impl Radar { .get_state() .landing_position(&input.ct) .unwrap(); - self.last_player_position = Point2 { x: pos.x, y: pos.y } + self.last_player_position = Point2::new(pos.x, pos.y) } ShipState::UnLanding { .. } => { @@ -56,15 +56,12 @@ impl Radar { .get_state() .unlanding_position(&input.ct) .unwrap(); - self.last_player_position = Point2 { x: pos.x, y: pos.y } + self.last_player_position = Point2::new(pos.x, pos.y) } ShipState::Landed { target } => { let landed_body = input.ct.get_system_object(*target); - self.last_player_position = Point2 { - x: landed_body.pos.x, - y: landed_body.pos.y, - }; + self.last_player_position = Point2::new(landed_body.pos.x, landed_body.pos.y); } ShipState::Flying | ShipState::Collapsing { .. } => { let player_body = input @@ -72,7 +69,7 @@ impl Radar { .get_rigid_body(player_ship.rigid_body) .unwrap(); - self.last_player_position = util::rigidbody_position(player_body) + self.last_player_position = (*player_body.translation()).into(); } }; @@ -106,10 +103,7 @@ impl Radar { let system = input.ct.get_system(input.current_system); for o in &system.objects { let size = (o.size / o.pos.z) / (radar_range * system_object_scale); - let p = Point2 { - x: o.pos.x, - y: o.pos.y, - }; + let p = Point2::new(o.pos.x, o.pos.y); let d = (p - self.last_player_position) / radar_range; // Add half the blip sprite's height to distance let m = d.magnitude() + (size / (2.0 * radar_size)); @@ -133,12 +127,10 @@ impl Radar { UiInstance::SIZE * state.vertex_buffers.ui_counter, bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwC.to_int(), - position: (Point2 { - x: radar_size / 2.0 + 10.0, - y: radar_size / -2.0 - 10.0, - } + (d * (radar_size / 2.0))) + position: (Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0) + + (d * (radar_size / 2.0))) .into(), - angle: o.angle.0, + angle: o.angle, size, color: [0.5, 0.5, 0.5, 1.0], sprite_index: planet_sprite.get_index(), @@ -171,7 +163,7 @@ impl Radar { }; let ship = input.ct.get_ship(s.data.get_content()); let size = (ship.size * ship.sprite.aspect) * ship_scale; - let p = util::rigidbody_position(r); + let p: Point2 = (*r.translation()).into(); let d = (p - self.last_player_position) / radar_range; let m = d.magnitude() + (size / (2.0 * radar_size)); if m < hide_range { @@ -179,12 +171,10 @@ impl Radar { if size < 2.0 { continue; } - let angle = util::rigidbody_rotation(r).angle(Vector2 { x: 0.0, y: 1.0 }); + let angle = r.rotation().angle(); - let position = Point2 { - x: radar_size / 2.0 + 10.0, - y: radar_size / -2.0 - 10.0, - } + (d * (radar_size / 2.0)); + let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0) + + (d * (radar_size / 2.0)); // Enforce buffer limit // TODO: cleaner solution. don't do this everywhere. @@ -200,7 +190,7 @@ impl Radar { bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwC.to_int(), position: position.into(), - angle: -angle.0, // TODO: consistent angles + angle: -angle, // TODO: consistent angles size, color, sprite_index: ship_sprite.get_index(), @@ -211,10 +201,10 @@ impl Radar { } // Draw viewport frame - let d = Vector2 { - x: (input.camera_zoom / 2.0) * state.window_aspect, - y: input.camera_zoom / 2.0, - } / radar_range; + let d = Vector2::new( + (input.camera_zoom / 2.0) * state.window_aspect, + input.camera_zoom / 2.0, + ) / radar_range; let m = d.magnitude(); let d = d * (radar_size / 2.0); let color = [0.3, 0.3, 0.3, 1.0]; @@ -233,10 +223,10 @@ impl Radar { UiInstance::SIZE * state.vertex_buffers.ui_counter, bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwNw.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) - d.x, - y: (radar_size / -2.0 - 10.0) + d.y, - } + position: Point2::new( + (radar_size / 2.0 + 10.0) - d.x, + (radar_size / -2.0 - 10.0) + d.y, + ) .into(), angle: 0.0, size, @@ -251,12 +241,12 @@ impl Radar { UiInstance::SIZE * state.vertex_buffers.ui_counter, bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwSw.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) - d.x, - y: (radar_size / -2.0 - 10.0) - d.y, - } + position: Point2::new( + (radar_size / 2.0 + 10.0) - d.x, + (radar_size / -2.0 - 10.0) - d.y, + ) .into(), - angle: Rad::from(Deg(90.0)).0, + angle: to_radians(90.0), size, color, sprite_index: sprite.get_index(), @@ -269,12 +259,12 @@ impl Radar { UiInstance::SIZE * state.vertex_buffers.ui_counter, bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwSe.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) + d.x, - y: (radar_size / -2.0 - 10.0) - d.y, - } + position: Point2::new( + (radar_size / 2.0 + 10.0) + d.x, + (radar_size / -2.0 - 10.0) - d.y, + ) .into(), - angle: Rad::from(Deg(180.0)).0, + angle: to_radians(180.0), size, color, sprite_index: sprite.get_index(), @@ -287,12 +277,12 @@ impl Radar { UiInstance::SIZE * state.vertex_buffers.ui_counter, bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwNe.to_int(), - position: Point2 { - x: (radar_size / 2.0 + 10.0) + d.x, - y: (radar_size / -2.0 - 10.0) + d.y, - } + position: Point2::new( + (radar_size / 2.0 + 10.0) + d.x, + (radar_size / -2.0 - 10.0) + d.y, + ) .into(), - angle: Rad::from(Deg(270.0)).0, + angle: to_radians(270.0), size, color, sprite_index: sprite.get_index(), @@ -302,15 +292,14 @@ impl Radar { } // Arrow to center of system - let q = Point2 { x: 0.0, y: 0.0 } - self.last_player_position; + let q = Point2::new(0.0, 0.0) - self.last_player_position; let m = q.magnitude(); if m > 200.0 { - let player_angle = q.angle(Vector2 { x: 0.0, y: 1.0 }); + let player_angle = q.angle(&Vector2::new(0.0, 1.0)); - let position: Point2 = Point2 { - x: radar_size / 2.0 + 10.0, - y: radar_size / -2.0 - 10.0, - } + ((q.normalize() * 0.865) * (radar_size / 2.0)); + let position: Point2 = + Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0) + + ((q.normalize() * 0.865) * (radar_size / 2.0)); if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. @@ -323,7 +312,7 @@ impl Radar { bytemuck::cast_slice(&[UiInstance { anchor: PositionAnchor::NwC.to_int(), position: position.into(), - angle: -player_angle.0, + angle: -player_angle, size: 10.0, color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)], sprite_index: arrow_sprite.get_index(), diff --git a/crates/system/Cargo.toml b/crates/system/Cargo.toml index 5761a2f..34a4bde 100644 --- a/crates/system/Cargo.toml +++ b/crates/system/Cargo.toml @@ -24,5 +24,4 @@ galactica-playeragent = { workspace = true } rapier2d = { workspace = true } nalgebra = { workspace = true } crossbeam = { workspace = true } -cgmath = { workspace = true } rand = { workspace = true } diff --git a/crates/system/src/data/ship/ship.rs b/crates/system/src/data/ship/ship.rs index 61c2c8c..8c0ba1b 100644 --- a/crates/system/src/data/ship/ship.rs +++ b/crates/system/src/data/ship/ship.rs @@ -1,9 +1,9 @@ +use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle, SystemObjectHandle}; +use nalgebra::{Point2, Point3}; +use rand::{rngs::ThreadRng, Rng}; use std::{collections::HashMap, time::Instant}; use super::{OutfitSet, ShipPersonality}; -use cgmath::{InnerSpace, Point2, Point3, Rad}; -use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle, SystemObjectHandle}; -use rand::{rngs::ThreadRng, Rng}; /// Ship state machine. /// Any ship we keep track of is in one of these states. @@ -38,8 +38,8 @@ pub enum ShipState { /// The point, in world coordinates, where we started from_position: Point2, - /// The ship's angle when we started landing - from_angle: Rad, + /// The ship's angle when we started landing, in radians + from_angle: f32, /// The planet we're landing on target: SystemObjectHandle, @@ -57,8 +57,8 @@ pub enum ShipState { /// The point, in world coordinates, to which we're going to_position: Point2, - /// The angle we'll be at when we arrive - to_angle: Rad, + /// The angle we'll be at when we arrive, in radians + to_angle: f32, /// The planet we're taking off from from: SystemObjectHandle, @@ -104,10 +104,7 @@ impl ShipState { } => Some({ let target = ct.get_system_object(*target); - let diff = Point2 { - x: target.pos.x, - y: target.pos.y, - } - from_position; + let diff = Point2::new(target.pos.x, target.pos.y) - from_position; let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8; // TODO: improve animation @@ -120,11 +117,11 @@ impl ShipState { let pos = from_position + (diff * (elapsed / total)); - Point3 { - x: pos.x, - y: pos.y, - z: 1.0 + ((target.pos.z - 1.0) * (elapsed / total)), - } + Point3::new( + pos.x, + pos.y, + 1.0 + ((target.pos.z - 1.0) * (elapsed / total)), + ) }), _ => None, } @@ -142,11 +139,7 @@ impl ShipState { } => Some({ let from = ct.get_system_object(*from); - let diff = to_position - - Point2 { - x: from.pos.x, - y: from.pos.y, - }; + let diff = to_position - Point2::new(from.pos.x, from.pos.y); //let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8; // TODO: improve animation @@ -157,16 +150,13 @@ impl ShipState { // TODO: time by distance // TODO: keep momentum - let pos = Point2 { - x: from.pos.x, - y: from.pos.y, - } + (diff * (elapsed / total)); + let pos = Point2::new(from.pos.x, from.pos.y) + (diff * (elapsed / total)); - Point3 { - x: pos.x, - y: pos.y, - z: from.pos.z + ((1.0 - from.pos.z) * (elapsed / total)), - } + Point3::new( + pos.x, + pos.y, + from.pos.z + ((1.0 - from.pos.z) * (elapsed / total)), + ) }), _ => None, } @@ -232,7 +222,7 @@ impl ShipData { &mut self, target: SystemObjectHandle, from_position: Point2, - from_angle: Rad, + from_angle: f32, ) -> bool { match self.state { ShipState::Flying => { @@ -257,7 +247,7 @@ impl ShipData { ShipState::Landed { target } => { self.state = ShipState::UnLanding { to_position, - to_angle: Rad(1.0), + to_angle: 1.0, from: target, total: 5.0, elapsed: 0.0, diff --git a/crates/system/src/phys/controller/point.rs b/crates/system/src/phys/controller/point.rs index fda5161..a6df781 100644 --- a/crates/system/src/phys/controller/point.rs +++ b/crates/system/src/phys/controller/point.rs @@ -1,5 +1,5 @@ -use cgmath::{Deg, InnerSpace}; use galactica_content::Relationship; +use nalgebra::{Rotation2, Vector2}; use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle}; use std::collections::HashMap; @@ -8,7 +8,7 @@ use crate::data::ShipState; use super::{ super::{ objects::{PhysSimShip, ShipControls}, - util, PhysStepResources, + PhysStepResources, }, ShipControllerStruct, }; @@ -37,8 +37,8 @@ impl ShipControllerStruct for PointShipController { let my_ship = ships.get(&this_ship).unwrap(); let this_rigidbody = rigid_bodies.get(my_ship.rigid_body).unwrap(); - let my_position = util::rigidbody_position(this_rigidbody); - let my_rotation = util::rigidbody_rotation(this_rigidbody); + let my_position = this_rigidbody.translation(); + let my_rotation = this_rigidbody.rotation(); let my_angvel = this_rigidbody.angvel(); let my_faction = res.ct.get_faction(my_ship.data.get_faction()); @@ -59,13 +59,13 @@ impl ShipControllerStruct for PointShipController { // Find the closest target let mut closest_enemy_position = match hostile_ships.next() { - Some(c) => util::rigidbody_position(c), + Some(c) => c.translation(), None => return Some(controls), // Do nothing if no targets are available }; let mut d = (my_position - closest_enemy_position).magnitude(); for r in hostile_ships { - let p = util::rigidbody_position(r); + let p = r.translation(); let new_d = (my_position - p).magnitude(); if new_d < d { d = new_d; @@ -73,13 +73,12 @@ impl ShipControllerStruct for PointShipController { } } - let angle_delta: Deg = (my_rotation) - .angle(closest_enemy_position - my_position) - .into(); + let angle = (closest_enemy_position - my_position).angle(&Vector2::new(1.0, 0.0)); + let angle_delta = my_rotation.angle_to(&Rotation2::new(angle).into()); - if angle_delta < Deg(0.0) && my_angvel > -0.3 { + if angle_delta < 0.0 && my_angvel > -0.3 { controls.right = true; - } else if angle_delta > Deg(0.0) && my_angvel < 0.3 { + } else if angle_delta > 0.0 && my_angvel < 0.3 { controls.left = true; } diff --git a/crates/system/src/phys/mod.rs b/crates/system/src/phys/mod.rs index fcfaac9..593cc6d 100644 --- a/crates/system/src/phys/mod.rs +++ b/crates/system/src/phys/mod.rs @@ -5,7 +5,6 @@ pub mod objects; mod particlebuilder; mod stepresources; mod systemsim; -pub mod util; mod wrapper; pub use particlebuilder::*; diff --git a/crates/system/src/phys/objects/collapse.rs b/crates/system/src/phys/objects/collapse.rs index 934efe3..c8fdd82 100644 --- a/crates/system/src/phys/objects/collapse.rs +++ b/crates/system/src/phys/objects/collapse.rs @@ -1,10 +1,9 @@ -use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero}; use galactica_content::{CollapseEvent, Ship}; -use nalgebra::point; +use nalgebra::{Point2, Vector2}; use rand::{rngs::ThreadRng, Rng}; use rapier2d::{dynamics::RigidBody, geometry::Collider}; -use super::super::{util, ParticleBuilder, PhysStepResources}; +use super::super::{ParticleBuilder, PhysStepResources}; use crate::data::ShipData; #[derive(Debug, Clone)] @@ -32,9 +31,9 @@ impl ShipCollapseSequence { while !a { y = self.rng.gen_range(-1.0..=1.0) * ship.size / 2.0; x = self.rng.gen_range(-1.0..=1.0) * ship.size * ship.sprite.aspect / 2.0; - a = collider.shape().contains_local_point(&point![x, y]); + a = collider.shape().contains_local_point(&Point2::new(x, y)); } - Vector2 { x, y } + Vector2::new(x, y) } /// Step this sequence `t` seconds @@ -46,9 +45,8 @@ impl ShipCollapseSequence { collider: &mut Collider, ) { let ship_content = res.ct.get_ship(ship_data.get_content()); - let ship_pos = util::rigidbody_position(rigid_body); - let ship_rot = util::rigidbody_rotation(rigid_body); - let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 }); + let ship_pos = rigid_body.translation(); + let ship_rot = rigid_body.rotation(); let (total, elapsed) = ship_data.get_state().collapse_state().unwrap(); // How much time has passed since step() was last called @@ -71,25 +69,22 @@ impl ShipCollapseSequence { let effect = res.ct.get_effect(spawner.effect); for _ in 0..spawner.count as usize { - let pos = if let Some(pos) = spawner.pos { - pos.to_vec() + let pos: Vector2 = if let Some(pos) = spawner.pos { + Vector2::new(pos.x, pos.y) } else { self.random_in_ship(ship_content, collider) }; - let pos = ship_pos - + (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos); + let pos = ship_pos + (ship_rot * pos); - let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]); + let velocity = + rigid_body.velocity_at_point(&Point2::new(pos.x, pos.y)); res.particles.push(ParticleBuilder::from_content( effect, - pos, - Rad::zero(), - Vector2 { - x: velocity.x, - y: velocity.y, - }, - Vector2::zero(), + pos.into(), + 0.0, + velocity, + Vector2::new(0.0, 0.0), )) } } @@ -121,21 +116,21 @@ impl ShipCollapseSequence { if self.rng.gen_range(0.0..=1.0) <= p_add { let pos = if let Some(pos) = spawner.pos { - pos.to_vec() + Vector2::new(pos.x, pos.y) } else { self.random_in_ship(ship_content, collider) }; // Position, adjusted for ship rotation - let pos = ship_pos + Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos; - let vel = rigid_body.velocity_at_point(&point![pos.x, pos.y]); + let pos = ship_pos + (ship_rot * pos); + let vel = rigid_body.velocity_at_point(&Point2::new(pos.x, pos.y)); res.particles.push(ParticleBuilder { sprite: effect.sprite, - pos, - velocity: Vector2 { x: vel.x, y: vel.y }, - angle: Rad::zero(), - angvel: Rad::zero(), + pos: pos.into(), + velocity: vel, + angle: 0.0, + angvel: 0.0, lifetime: effect.lifetime, size: effect.size, fade: 0.0, diff --git a/crates/system/src/phys/objects/ship.rs b/crates/system/src/phys/objects/ship.rs index c4b7dd9..6041137 100644 --- a/crates/system/src/phys/objects/ship.rs +++ b/crates/system/src/phys/objects/ship.rs @@ -1,6 +1,5 @@ -use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero}; use galactica_content::{Content, FactionHandle, ShipHandle}; -use nalgebra::{point, vector}; +use nalgebra::{point, vector, Rotation2, Vector2}; use rand::Rng; use rapier2d::{ dynamics::{RigidBody, RigidBodyHandle}, @@ -10,7 +9,7 @@ use rapier2d::{ use crate::data::{ShipData, ShipPersonality, ShipState}; use super::{ - super::{util, ParticleBuilder, PhysStepResources}, + super::{ParticleBuilder, PhysStepResources}, collapse::ShipCollapseSequence, }; @@ -114,9 +113,9 @@ impl PhysSimShip { collider: &mut Collider, ) { let ship_content = res.ct.get_ship(self.data.get_content()); - let ship_pos = util::rigidbody_position(&rigid_body); - let ship_rot = util::rigidbody_rotation(rigid_body); - let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 }); + let ship_pos = rigid_body.translation(); + let ship_rot = rigid_body.rotation(); + let ship_ang = ship_rot.angle(); let mut rng = rand::thread_rng(); if self.data.get_hull() <= ship_content.damage.hull { @@ -125,7 +124,7 @@ impl PhysSimShip { let effect = res.ct.get_effect(e.effect); let pos = if let Some(pos) = e.pos { - pos.to_vec() + Vector2::new(pos.x, pos.y) } else { // Pick a random point inside this ship's collider let mut y = 0.0; @@ -138,29 +137,24 @@ impl PhysSimShip { / 2.0; a = collider.shape().contains_local_point(&point![x, y]); } - Vector2 { x, y } + Vector2::new(x, y) }; - let pos = - ship_pos + (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos); - + let pos = ship_pos + (Rotation2::new(ship_ang) * pos); let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]); res.particles.push(ParticleBuilder::from_content( effect, - pos, - Rad::zero(), - Vector2 { - x: velocity.x, - y: velocity.y, - }, - Vector2::zero(), + pos.into(), + 0.0, + velocity, + Vector2::new(0.0, 0.0), )) } } } - let engine_force = ship_rot * res.t; + let engine_force = ship_rot * (Vector2::new(1.0, 0.0) * res.t); if self.controls.thrust { rigid_body.apply_impulse( diff --git a/crates/system/src/phys/particlebuilder.rs b/crates/system/src/phys/particlebuilder.rs index bef138f..402f3ab 100644 --- a/crates/system/src/phys/particlebuilder.rs +++ b/crates/system/src/phys/particlebuilder.rs @@ -1,5 +1,5 @@ -use cgmath::{Matrix2, Point2, Rad, Vector2}; use galactica_content::{Effect, SpriteHandle}; +use nalgebra::{Point2, Rotation2, Vector2}; use rand::Rng; /// Instructions to create a new particle @@ -14,11 +14,11 @@ pub struct ParticleBuilder { /// This particle's velocity, in world coordinates pub velocity: Vector2, - /// This particle's angle - pub angle: Rad, + /// This particle's angle, in radians + pub angle: f32, /// This particle's angular velocity (rad/sec) - pub angvel: Rad, + pub angvel: f32, /// This particle's lifetime, in seconds pub lifetime: f32, @@ -36,7 +36,7 @@ impl ParticleBuilder { pub fn from_content( effect: &Effect, pos: Point2, - parent_angle: Rad, + parent_angle: f32, parent_velocity: Vector2, target_velocity: Vector2, ) -> Self { @@ -51,21 +51,18 @@ impl ParticleBuilder { let velocity = ((effect.velocity_scale_parent + a) * parent_velocity) + ((effect.velocity_scale_target + b) * target_velocity); - Matrix2::from_angle(Rad( - rng.gen_range(-effect.direction_rng..=effect.direction_rng) - )) * velocity + Rotation2::new(rng.gen_range(-effect.direction_rng..=effect.direction_rng)) * velocity }; - // Rad has odd behavior when its angle is zero, so we need extra checks here let angvel = if effect.angvel_rng == 0.0 { effect.angvel } else { - Rad(effect.angvel.0 + rng.gen_range(-effect.angvel_rng..=effect.angvel_rng)) + effect.angvel + rng.gen_range(-effect.angvel_rng..=effect.angvel_rng) }; let angle = if effect.angle_rng == 0.0 { parent_angle + effect.angle } else { - parent_angle + effect.angle + Rad(rng.gen_range(-effect.angle_rng..=effect.angle_rng)) + parent_angle + effect.angle + rng.gen_range(-effect.angle_rng..=effect.angle_rng) }; ParticleBuilder { diff --git a/crates/system/src/phys/systemsim.rs b/crates/system/src/phys/systemsim.rs index 51f6d35..d26e640 100644 --- a/crates/system/src/phys/systemsim.rs +++ b/crates/system/src/phys/systemsim.rs @@ -1,24 +1,23 @@ -use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero}; use galactica_content::{ Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle, SystemHandle, SystemObjectHandle, }; use galactica_playeragent::PlayerAgent; -use nalgebra::{point, vector}; +use nalgebra::{point, vector, Point2, Rotation2, Vector2}; use rand::Rng; use rapier2d::{ dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet}, geometry::{ColliderBuilder, ColliderHandle, ColliderSet}, pipeline::ActiveEvents, }; -use std::{collections::HashMap, f32::consts::PI}; +use std::collections::HashMap; use crate::data::{ShipPersonality, ShipState}; use super::{ controller::ShipController, objects::{PhysProjectile, PhysSimShip}, - util, ParticleBuilder, PhysStepResources, + ParticleBuilder, PhysStepResources, }; // TODO: replace with a more generic handle @@ -71,11 +70,8 @@ impl<'a> PhysSim { let ship = self.ships.get_mut(&collider).unwrap(); let r = self.rigid_body_set.get(ship.rigid_body).unwrap(); - ship.data.land_on( - target, - util::rigidbody_position(r), - -util::rigidbody_rotation(r).angle(Vector2 { x: 0.0, y: 1.0 }), - ); + ship.data + .land_on(target, (*r.translation()).into(), r.rotation().angle()); let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); r.set_enabled(false); @@ -93,10 +89,7 @@ impl<'a> PhysSim { let obj = ship.data.get_state().landed_on().unwrap(); let obj = ct.get_system_object(obj); - let target_pos = Point2 { - x: obj.pos.x + 100.0, - y: obj.pos.y + 100.0, - }; + let target_pos = Point2::new(obj.pos.x + 100.0, obj.pos.y + 100.0); ship.data.unland(target_pos); @@ -164,8 +157,9 @@ impl<'a> PhysSim { if destory_projectile { let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); - let v = util::rigidbody_velocity(pr).normalize() * projectile.content.force; - let pos = util::rigidbody_position(pr); + let v = + pr.velocity_at_point(pr.center_of_mass()).normalize() * projectile.content.force; + let pos = *pr.translation(); let _ = pr; let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); @@ -173,8 +167,8 @@ impl<'a> PhysSim { // Borrow again, we can only have one at a time let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); - let pos = util::rigidbody_position(pr); - let angle = util::rigidbody_rotation(pr).angle(Vector2 { x: 1.0, y: 0.0 }); + let pos = *pr.translation(); + let angle = pr.rotation().angle(); match &projectile.content.impact_effect { None => {} @@ -182,19 +176,16 @@ impl<'a> PhysSim { let effect = res.ct.get_effect(*x); let r = ship.rigid_body; let sr = self.get_rigid_body(r).unwrap(); - let parent_velocity = util::rigidbody_velocity(pr); + let parent_velocity = pr.velocity_at_point(pr.center_of_mass()); let target_velocity = sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y)); res.particles.push(ParticleBuilder::from_content( effect, - pos, + pos.into(), -angle, parent_velocity, - Vector2 { - x: target_velocity.x, - y: target_velocity.y, - }, + target_velocity, )); } }; @@ -228,15 +219,8 @@ impl PhysSim { position: Point2, ) -> PhysSimShipHandle { let ship_content = ct.get_ship(handle); - let cl = ColliderBuilder::convex_decomposition( - &ship_content.collision.points[..], - &ship_content.collision.indices[..], - ) - // Rotate collider to match sprite - // (Collider starts pointing east, sprite starts pointing north.) - .rotation(PI / -2.0) - .mass(ship_content.mass); - // TODO: only build colliders once + let cl = ship_content.collider.0.clone(); + // TODO: additonal ship mass from outfits and cargo let rb = RigidBodyBuilder::dynamic() .angular_damping(ship_content.angular_drag) @@ -247,7 +231,7 @@ impl PhysSim { let ridid_body = self.rigid_body_set.insert(rb.build()); let collider = self.collider_set - .insert_with_parent(cl.build(), ridid_body, &mut self.rigid_body_set); + .insert_with_parent(cl, ridid_body, &mut self.rigid_body_set); self.ship_behaviors.insert( collider, @@ -379,34 +363,31 @@ impl PhysSim { let ship = self.ships.get(&collider).unwrap(); let rigid_body = self.get_rigid_body(ship.rigid_body).unwrap(); - let ship_pos = util::rigidbody_position(rigid_body); - let ship_rot = util::rigidbody_rotation(rigid_body); - let ship_vel = util::rigidbody_velocity(rigid_body); - let ship_ang = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); + let ship_pos = rigid_body.translation(); + let ship_rot = rigid_body.rotation(); + let ship_ang = ship_rot.angle(); + let ship_vel = rigid_body.velocity_at_point(rigid_body.center_of_mass()); - let pos = ship_pos + (Matrix2::from_angle(-ship_ang) * gun_point.pos.to_vec()); + let pos = ship_pos + (ship_rot * gun_point.pos); let outfit = res.ct.get_outfit(outfit); let outfit = outfit.gun.as_ref().unwrap(); - let spread: Rad = - Deg(rng.gen_range(-outfit.projectile.angle_rng.0..=outfit.projectile.angle_rng.0)) - .into(); - + let spread = rng.gen_range(-outfit.projectile.angle_rng..=outfit.projectile.angle_rng); let vel = ship_vel - + (Matrix2::from_angle(-ship_ang + spread) - * Vector2 { - x: 0.0, - y: outfit.projectile.speed + + (Rotation2::new(ship_ang + spread) + * Vector2::new( + outfit.projectile.speed + rng.gen_range( -outfit.projectile.speed_rng..=outfit.projectile.speed_rng, ), - }); + 0.0, + )); let rigid_body = RigidBodyBuilder::kinematic_velocity_based() .translation(vector![pos.x, pos.y]) - .rotation(-ship_ang.0) - .linvel(vector![vel.x, vel.y]) + .rotation(ship_ang) + .linvel(vel) .build(); let collider = match &outfit.projectile.collider { @@ -487,9 +468,9 @@ impl PhysSim { None => {} Some(x) => { let x = res.ct.get_effect(*x); - let pos = util::rigidbody_position(&pr); - let vel = util::rigidbody_velocity(&pr); - let angle = util::rigidbody_rotation(&pr).angle(Vector2 { x: 1.0, y: 0.0 }); + let pos = *pr.translation(); + let vel = pr.velocity_at_point(pr.center_of_mass()); + let angle = pr.rotation().angle(); let velocity = { let a = rng @@ -502,10 +483,10 @@ impl PhysSim { res.particles.push(ParticleBuilder::from_content( x, - pos, + pos.into(), -angle, velocity, - Vector2::zero(), + Vector2::new(0.0, 0.0), )); } }; diff --git a/crates/system/src/phys/util.rs b/crates/system/src/phys/util.rs deleted file mode 100644 index 6674fb4..0000000 --- a/crates/system/src/phys/util.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Conversion utilities - -use cgmath::{Point2, Vector2}; -use nalgebra; -use rapier2d::dynamics::RigidBody; - -// TODO: Migrate to nalgebra fully, remove these converters - -/// Convert a rigidbody position to a position in game coordinates -pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2 { - Point2 { - x: r.translation()[0], - y: r.translation()[1], - } -} - -/// Convert a rigidbody rotation to a rotation in game coordinates -pub fn rigidbody_rotation(r: &RigidBody) -> Vector2 { - Vector2 { - x: r.rotation().re, - y: r.rotation().im, - } -} - -/// Convert a rigidbody velocity to a velocity in game coordinates -pub fn rigidbody_velocity(r: &RigidBody) -> cgmath::Vector2 { - let v = r.velocity_at_point(&nalgebra::Point2::new( - r.translation()[0], - r.translation()[1], - )); - Vector2 { x: v.x, y: v.y } -} diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 4753b11..16cf93c 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -4,3 +4,8 @@ pub mod constants; pub mod timing; + +/// Convert an angle in degrees to radians +pub fn to_radians(degrees: f32) -> f32 { + return (degrees / 360.0) * std::f32::consts::TAU; +}