Compare commits

..

2 Commits

Author SHA1 Message Date
Mark d014e085d5
Removed cgmath 2024-01-12 22:47:40 -08:00
Mark c656a2768d
Updated TODO 2024-01-12 22:47:24 -08:00
33 changed files with 350 additions and 456 deletions

37
Cargo.lock generated
View File

@ -90,15 +90,6 @@ version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" 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]] [[package]]
name = "approx" name = "approx"
version = "0.5.1" version = "0.5.1"
@ -276,16 +267,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 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]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@ -641,12 +622,12 @@ name = "galactica"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cgmath",
"galactica-content", "galactica-content",
"galactica-playeragent", "galactica-playeragent",
"galactica-render", "galactica-render",
"galactica-system", "galactica-system",
"galactica-util", "galactica-util",
"nalgebra",
"pollster", "pollster",
"rand", "rand",
"wgpu", "wgpu",
@ -658,10 +639,11 @@ name = "galactica-content"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cgmath",
"galactica-packer", "galactica-packer",
"galactica-util",
"image", "image",
"nalgebra", "nalgebra",
"rapier2d",
"serde", "serde",
"toml", "toml",
"walkdir", "walkdir",
@ -684,9 +666,9 @@ name = "galactica-playeragent"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cgmath",
"galactica-content", "galactica-content",
"galactica-util", "galactica-util",
"nalgebra",
"pollster", "pollster",
"rapier2d", "rapier2d",
"wgpu", "wgpu",
@ -699,7 +681,6 @@ version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytemuck", "bytemuck",
"cgmath",
"galactica-content", "galactica-content",
"galactica-packer", "galactica-packer",
"galactica-playeragent", "galactica-playeragent",
@ -707,6 +688,7 @@ dependencies = [
"galactica-util", "galactica-util",
"glyphon", "glyphon",
"image", "image",
"nalgebra",
"rand", "rand",
"wgpu", "wgpu",
"winit", "winit",
@ -716,7 +698,6 @@ dependencies = [
name = "galactica-system" name = "galactica-system"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"cgmath",
"crossbeam", "crossbeam",
"galactica-content", "galactica-content",
"galactica-playeragent", "galactica-playeragent",
@ -1181,7 +1162,7 @@ version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa"
dependencies = [ dependencies = [
"approx 0.5.1", "approx",
"matrixmultiply", "matrixmultiply",
"nalgebra-macros", "nalgebra-macros",
"num-complex", "num-complex",
@ -1465,7 +1446,7 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "104ae65232e20477a98f9f1e75ca9850eae24a2ea846a2b1a0af03ad752136ce" checksum = "104ae65232e20477a98f9f1e75ca9850eae24a2ea846a2b1a0af03ad752136ce"
dependencies = [ dependencies = [
"approx 0.5.1", "approx",
"arrayvec", "arrayvec",
"bitflags 1.3.2", "bitflags 1.3.2",
"downcast-rs", "downcast-rs",
@ -1620,7 +1601,7 @@ version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f94d294a9b96694c14888dd0e8ce77620dcc4f2f49264109ef835fa5e2285b84" checksum = "f94d294a9b96694c14888dd0e8ce77620dcc4f2f49264109ef835fa5e2285b84"
dependencies = [ dependencies = [
"approx 0.5.1", "approx",
"arrayvec", "arrayvec",
"bit-vec", "bit-vec",
"bitflags 1.3.2", "bitflags 1.3.2",
@ -1818,7 +1799,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae"
dependencies = [ dependencies = [
"approx 0.5.1", "approx",
"num-complex", "num-complex",
"num-traits", "num-traits",
"paste", "paste",

View File

@ -61,8 +61,6 @@ nalgebra = "0.32.3"
crossbeam = "0.8.3" crossbeam = "0.8.3"
pollster = "0.3" pollster = "0.3"
anyhow = "1.0" anyhow = "1.0"
# TODO: migrate to nalgebra
cgmath = "0.18.0"
rand = "0.8.5" rand = "0.8.5"
walkdir = "2.4.0" walkdir = "2.4.0"
toml = "0.8.8" toml = "0.8.8"

View File

@ -153,6 +153,7 @@
- How big should sprite resolutions be? How about frame rate? - How big should sprite resolutions be? How about frame rate?
- Naming: atlas, sprite, image, frame, texture - Naming: atlas, sprite, image, frame, texture
- Outfits may not change unless you've landed. They might not change ever for CC ships! - Outfits may not change unless you've landed. They might not change ever for CC ships!
- All angle adjustments happen in content
## Ideas ## Ideas

View File

@ -18,11 +18,12 @@ workspace = true
[dependencies] [dependencies]
galactica-packer = { workspace = true } galactica-packer = { workspace = true }
galactica-util = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
toml = { workspace = true } toml = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
cgmath = { workspace = true }
walkdir = { workspace = true } walkdir = { workspace = true }
nalgebra = { workspace = true } nalgebra = { workspace = true }
image = { workspace = true } image = { workspace = true }
rapier2d = { workspace = true }

View File

@ -1,12 +1,11 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use cgmath::Rad;
use std::collections::HashMap; use std::collections::HashMap;
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle}; use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle};
pub(crate) mod syntax { pub(crate) mod syntax {
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cgmath::Deg; use galactica_util::to_radians;
use serde::Deserialize; use serde::Deserialize;
use crate::{Content, ContentBuildContext, EffectHandle}; use crate::{Content, ContentBuildContext, EffectHandle};
@ -75,10 +74,10 @@ pub(crate) mod syntax {
size_rng: self.size_rng.unwrap_or(0.0), size_rng: self.size_rng.unwrap_or(0.0),
lifetime, lifetime,
lifetime_rng: self.lifetime_rng.unwrap_or(0.0), lifetime_rng: self.lifetime_rng.unwrap_or(0.0),
angle: Deg(self.angle.unwrap_or(0.0) / 2.0).into(), angle: to_radians(self.angle.unwrap_or(0.0) / 2.0),
angle_rng: self.angle_rng.unwrap_or(0.0) / 2.0, angle_rng: to_radians(self.angle_rng.unwrap_or(0.0) / 2.0),
angvel: Deg(self.angvel.unwrap_or(0.0)).into(), angvel: to_radians(self.angvel.unwrap_or(0.0)),
angvel_rng: self.angvel_rng.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: self.velocity_scale_parent.unwrap_or(0.0),
velocity_scale_parent_rng: self.velocity_scale_parent_rng.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), velocity_scale_target: self.velocity_scale_target.unwrap_or(0.0),
@ -141,14 +140,14 @@ pub struct Effect {
/// Random lifetime variation /// Random lifetime variation
pub lifetime_rng: f32, pub lifetime_rng: f32,
/// The angle this particle points once spawned /// The angle this particle points once spawned, in radians
pub angle: Rad<f32>, pub angle: f32,
/// Random angle variation /// Random angle variation, in radians
pub angle_rng: f32, pub angle_rng: f32,
/// How fast this particle spins /// How fast this particle spins, in radians/sec
pub angvel: Rad<f32>, pub angvel: f32,
/// Random angvel variation /// Random angvel variation
pub angvel_rng: f32, pub angvel_rng: f32,

View File

@ -1,8 +1,6 @@
use std::collections::HashMap;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use cgmath::Rad;
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap;
use crate::{ use crate::{
handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace, handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace,
@ -11,7 +9,7 @@ use crate::{
pub(crate) mod syntax { pub(crate) mod syntax {
use crate::{effect, part::outfitspace, ContentBuildContext}; use crate::{effect, part::outfitspace, ContentBuildContext};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cgmath::Deg; use galactica_util::to_radians;
use serde::Deserialize; use serde::Deserialize;
// Raw serde syntax structs. // Raw serde syntax structs.
// These are never seen by code outside this crate. // 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. // Divide by 2, so the angle matches the angle of the fire cone.
// This should ALWAYS be done in the content parser. // 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, impact_effect,
expire_effect, expire_effect,
collider: self.projectile.collider, collider: self.projectile.collider,
@ -214,8 +212,8 @@ pub struct Projectile {
/// The force this projectile applies /// The force this projectile applies
pub force: f32, pub force: f32,
/// The angle variation of this projectile. /// The angle variation of this projectile, in radians
pub angle_rng: Rad<f32>, pub angle_rng: f32,
/// The particle this projectile will spawn when it hits something /// The particle this projectile will spawn when it hits something
pub impact_effect: Option<EffectHandle>, pub impact_effect: Option<EffectHandle>,

View File

@ -1,8 +1,8 @@
use std::{collections::HashMap, hash::Hash};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use cgmath::Point2; use galactica_util::to_radians;
use nalgebra::{point, Point}; 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}; use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitSpace};
@ -116,7 +116,7 @@ pub struct Ship {
pub hull: f32, pub hull: f32,
/// Collision shape for this ship /// Collision shape for this ship
pub collision: Collision, pub collider: CollisionDebugWrapper,
/// Remove later /// Remove later
pub aspect: f32, pub aspect: f32,
@ -137,11 +137,15 @@ pub struct Ship {
pub damage: ShipDamage, pub damage: ShipDamage,
} }
/// Collision shape for this ship /// Hack to give `Collider` a fake debug method.
#[derive(Debug, Clone)] /// Pretend this is transparent, get the collider with .0.
pub struct Collision { #[derive(Clone)]
pub points: Vec<Point<f32, 2>>, pub struct CollisionDebugWrapper(pub Collider);
pub indices: Vec<[u32; 2]>,
impl Debug for CollisionDebugWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"CollisionDebugWrapper".fmt(f)
}
} }
/// An engine point on a ship. /// An engine point on a ship.
@ -150,7 +154,7 @@ pub struct Collision {
pub struct EnginePoint { pub struct EnginePoint {
/// This engine point's position, in game units, /// This engine point's position, in game units,
/// relative to the ship's center. /// relative to the ship's center.
pub pos: Point2<f32>, pub pos: Vector2<f32>,
/// The size of the flare that should be drawn /// The size of the flare that should be drawn
/// at this point, measured as height in game units. /// at this point, measured as height in game units.
@ -165,7 +169,7 @@ pub struct GunPoint {
/// This gun point's position, in game units, /// This gun point's position, in game units,
/// relative to the ship's center. /// relative to the ship's center.
pub pos: Point2<f32>, pub pos: Vector2<f32>,
} }
impl Eq for GunPoint {} impl Eq for GunPoint {}
@ -281,9 +285,8 @@ impl crate::Build for Ship {
.to_handle(build_context, ct) .to_handle(build_context, ct)
.with_context(|| format!("while loading ship `{}`", ship_name))?, .with_context(|| format!("while loading ship `{}`", ship_name))?,
count: e.count, count: e.count,
pos: e.pos.map(|p| Point2 { pos: e.pos.map(|p| {
x: p[0] * (size / 2.0) * aspect, Point2::new(p[0] * (size / 2.0) * aspect, p[1] * size / 2.0)
y: p[1] * size / 2.0,
}), }),
}); });
} }
@ -302,9 +305,11 @@ impl crate::Build for Ship {
format!("while loading ship `{}`", ship_name) format!("while loading ship `{}`", ship_name)
})?, })?,
count: g.count, count: g.count,
pos: g.pos.map(|p| Point2 { pos: g.pos.map(|p| {
x: p[0] * (size / 2.0) * aspect, Point2::new(
y: p[1] * size / 2.0, 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) .to_handle(build_context, ct)
.with_context(|| format!("while loading ship `{}`", ship_name))?, .with_context(|| format!("while loading ship `{}`", ship_name))?,
frequency: e.frequency, frequency: e.frequency,
pos: e.pos.map(|p| Point2 { pos: e.pos.map(|p| {
x: p[0] * (size / 2.0) * aspect, Point2::new(p[0] * (size / 2.0) * aspect, p[1] * size / 2.0)
y: p[1] * size / 2.0,
}), }),
}); });
} }
@ -367,13 +371,49 @@ impl crate::Build for Ship {
for g in ship.guns { for g in ship.guns {
guns.push(GunPoint { guns.push(GunPoint {
idx: guns.len() as u32, idx: guns.len() as u32,
pos: Point2 {
x: g.x * size * aspect / 2.0, // Angle adjustment, since sprites point north
y: g.y * size / 2.0, // 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<Point2<f32>> = 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 { ct.ships.push(Self {
aspect, aspect,
collapse, collapse,
@ -391,35 +431,14 @@ impl crate::Build for Ship {
.engines .engines
.iter() .iter()
.map(|e| EnginePoint { .map(|e| EnginePoint {
pos: Point2 { pos: Vector2::new(e.x * size * aspect / 2.0, e.y * size / 2.0),
x: e.x * size * aspect / 2.0,
y: e.y * size / 2.0,
},
size: e.size, size: e.size,
}) })
.collect(), .collect(),
guns, guns,
collision: Collision { collider: CollisionDebugWrapper(collider),
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(),
},
}); });
} }

View File

@ -1,5 +1,6 @@
use anyhow::{bail, Context, Result}; 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 std::collections::{HashMap, HashSet};
use crate::{ use crate::{
@ -115,8 +116,8 @@ pub struct SystemObject {
/// relative to the system's center (0, 0). /// relative to the system's center (0, 0).
pub pos: Point3<f32>, pub pos: Point3<f32>,
/// This object's sprite's angle. /// This object's sprite's angle, in radians
pub angle: Rad<f32>, pub angle: f32,
} }
/// Helper function for resolve_position, never called on its own. /// 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 r = resolve_coordinates(&objects, &three, cycle_detector)?;
let plane = Polar { let plane = Polar {
center: (r.x, r.y).into(), center: Point2::new(r.x, r.y),
radius: p.radius, radius: p.radius,
angle: Deg(p.angle).into(), angle: to_radians(p.angle),
} }
.to_cartesian(); .to_cartesian();
Ok(Point3 { Ok(Point3::new(plane.x, plane.y, p.z))
x: plane.x,
y: plane.y,
z: p.z,
})
} }
} }
} }
@ -216,7 +213,7 @@ impl crate::Build for System {
pos: resolve_position(&system.object, &obj, cycle_detector) pos: resolve_position(&system.object, &obj, cycle_detector)
.with_context(|| format!("In object {:#?}", label))?, .with_context(|| format!("In object {:#?}", label))?,
size: obj.size, size: obj.size,
angle: Deg(obj.angle.unwrap_or(0.0)).into(), angle: to_radians(obj.angle.unwrap_or(0.0)),
handle: SystemObjectHandle { handle: SystemObjectHandle {
system_handle, system_handle,
body_index: 0, body_index: 0,

View File

@ -1,18 +1,23 @@
use cgmath::{Angle, Point2, Rad, Vector2}; use nalgebra::{Point2, Vector2};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Polar { pub struct Polar {
/// The center of this polar coordinate
pub center: Point2<f32>, pub center: Point2<f32>,
/// The radius of this polar coordinate
pub radius: f32, pub radius: f32,
pub angle: Rad<f32>,
/// In radians
pub angle: f32,
} }
impl Polar { impl Polar {
pub fn to_cartesian(self) -> Point2<f32> { pub fn to_cartesian(self) -> Point2<f32> {
let v = Vector2 { let v = Vector2::new(
x: self.radius * self.angle.sin(), self.radius * self.angle.sin(),
y: self.radius * self.angle.cos(), self.radius * self.angle.cos(),
}; );
return self.center + v; return self.center + v;
} }

View File

@ -32,4 +32,4 @@ winit = { workspace = true }
wgpu = { workspace = true } wgpu = { workspace = true }
pollster = { workspace = true } pollster = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
cgmath = { workspace = true } nalgebra = { workspace = true }

View File

@ -1,4 +1,3 @@
use cgmath::Point2;
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle}; use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
use galactica_playeragent::PlayerAgent; use galactica_playeragent::PlayerAgent;
use galactica_system::data::ShipPersonality; use galactica_system::data::ShipPersonality;
@ -6,6 +5,7 @@ use galactica_system::phys::{
ParticleBuilder, PhysSim, PhysSimShipHandle, PhysStepResources, Wrapper, ParticleBuilder, PhysSim, PhysSimShipHandle, PhysStepResources, Wrapper,
}; };
use galactica_util::timing::Timing; use galactica_util::timing::Timing;
use nalgebra::Point2;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use std::time::Instant; use std::time::Instant;
@ -44,7 +44,7 @@ impl<'a> Game {
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 0 }, FactionHandle { index: 0 },
ShipPersonality::Player, 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(); let s = self.state.systemsim.get_ship_mut(&player).unwrap();
@ -66,7 +66,7 @@ impl<'a> Game {
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 1 }, FactionHandle { index: 1 },
ShipPersonality::Point, ShipPersonality::Point,
Point2 { x: 100.0, y: 0.0 }, Point2::new(100.0, 0.0),
); );
let s = systemsim.get_ship_mut(&a).unwrap(); let s = systemsim.get_ship_mut(&a).unwrap();
@ -78,8 +78,8 @@ impl<'a> Game {
&ct, &ct,
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 0 }, FactionHandle { index: 0 },
ShipPersonality::Point, ShipPersonality::Dummy,
Point2 { x: 0.0, y: 120.0 }, Point2::new(0.0, 120.0),
); );
let s = systemsim.get_ship_mut(&a).unwrap(); let s = systemsim.get_ship_mut(&a).unwrap();

View File

@ -1,15 +1,12 @@
mod game; mod game;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cgmath::Point2;
use galactica_content::{Content, SystemHandle}; use galactica_content::{Content, SystemHandle};
use galactica_playeragent::{PlayerAgent, PlayerStatus}; use galactica_playeragent::{PlayerAgent, PlayerStatus};
use galactica_render::RenderInput; use galactica_render::RenderInput;
use galactica_system::{ use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
data::ShipState,
phys::{util, PhysSimShipHandle},
};
use galactica_util::constants::ASSET_CACHE; use galactica_util::constants::ASSET_CACHE;
use nalgebra::Vector2;
use std::{ use std::{
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -92,7 +89,7 @@ fn main() -> Result<()> {
let r = let r =
&game.get_state().systemsim.get_rigid_body(o.rigid_body); &game.get_state().systemsim.get_rigid_body(o.rigid_body);
if let Some(r) = r { if let Some(r) = r {
Some(util::rigidbody_position(r)) Some(*r.translation())
} else { } else {
None None
} }
@ -100,19 +97,16 @@ fn main() -> Result<()> {
ShipState::UnLanding { .. } => { ShipState::UnLanding { .. } => {
let pos = let pos =
o.data.get_state().unlanding_position(&content).unwrap(); 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 { .. } => { ShipState::Landing { .. } => {
let pos = let pos =
o.data.get_state().landing_position(&content).unwrap(); 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 } => { ShipState::Landed { target } => {
let b = content.get_system_object(*target); let b = content.get_system_object(*target);
Some(Point2 { Some(Vector2::new(b.pos.x, b.pos.y))
x: b.pos.x,
y: b.pos.y,
})
} }
ShipState::Dead => None, ShipState::Dead => None,

View File

@ -24,5 +24,5 @@ winit = { workspace = true }
wgpu = { workspace = true } wgpu = { workspace = true }
pollster = { workspace = true } pollster = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
cgmath = { workspace = true } nalgebra = { workspace = true }
rapier2d = { workspace = true } rapier2d = { workspace = true }

View File

@ -1,9 +1,9 @@
use cgmath::Point2; use nalgebra::Vector2;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Camera { pub struct Camera {
/// Camera center /// Camera center
pub pos: Point2<f32>, pub pos: Vector2<f32>,
/// Camera zoom /// Camera zoom
/// (How many game units tall is the viewport?) /// (How many game units tall is the viewport?)
@ -16,7 +16,7 @@ pub struct Camera {
impl Camera { impl Camera {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
pos: (0.0, 0.0).into(), pos: Vector2::new(0.0, 0.0),
zoom: 500.0, zoom: 500.0,
aspect: 1.0, aspect: 1.0,
} }

View File

@ -1,5 +1,5 @@
use cgmath::Point2; use nalgebra::Vector2;
pub struct PlayerStatus { pub struct PlayerStatus {
pub pos: Option<Point2<f32>>, pub pos: Option<Vector2<f32>>,
} }

View File

@ -24,7 +24,7 @@ galactica-system = { workspace = true }
galactica-playeragent = { workspace = true } galactica-playeragent = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
cgmath = { workspace = true } nalgebra = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
image = { workspace = true } image = { workspace = true }
winit = { workspace = true } winit = { workspace = true }

View File

@ -41,8 +41,8 @@ fn transform_vertex(obj: ObjectData, vertex_position: vec2<f32>, sprite_index: u
// Apply rotation // Apply rotation
pos = mat2x2( pos = mat2x2(
vec2(cos(obj.angle), sin(obj.angle)), vec2(cos(obj.angle - 1.5708), sin(obj.angle - 1.5708)),
vec2(-sin(obj.angle), cos(obj.angle)) vec2(-sin(obj.angle - 1.5708), cos(obj.angle - 1.5708))
) * pos; ) * pos;
@ -81,8 +81,8 @@ fn transform_vertex(obj: ObjectData, vertex_position: vec2<f32>, sprite_index: u
// Apply parent's rotation // Apply parent's rotation
pos = mat2x2( pos = mat2x2(
vec2(cos(parent.angle), sin(parent.angle)), vec2(cos(parent.angle - 1.5708), sin(parent.angle - 1.5708)),
vec2(-sin(parent.angle), cos(parent.angle)) vec2(-sin(parent.angle - 1.5708), cos(parent.angle - 1.5708))
) * pos; ) * pos;
// Correct for screen aspect, preserving height // Correct for screen aspect, preserving height

View File

@ -1,9 +1,9 @@
use cgmath::Point2;
use galactica_content::{Content, SystemHandle}; use galactica_content::{Content, SystemHandle};
use galactica_playeragent::PlayerAgent; use galactica_playeragent::PlayerAgent;
use galactica_system::phys::{ParticleBuilder, PhysSim}; use galactica_system::phys::{ParticleBuilder, PhysSim};
use galactica_util::timing::Timing; use galactica_util::timing::Timing;
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
use nalgebra::Vector2;
use std::rc::Rc; use std::rc::Rc;
use wgpu::BufferAddress; use wgpu::BufferAddress;
use winit::window::Window; use winit::window::Window;
@ -13,7 +13,7 @@ use crate::{globaluniform::GlobalUniform, vertexbuffer::VertexBuffer};
/// Bundles parameters passed to a single call to GPUState::render /// Bundles parameters passed to a single call to GPUState::render
pub struct RenderInput<'a> { pub struct RenderInput<'a> {
/// Camera position, in world units /// Camera position, in world units
pub camera_pos: Point2<f32>, pub camera_pos: Vector2<f32>,
/// Player ship data /// Player ship data
pub player: &'a PlayerAgent, pub player: &'a PlayerAgent,

View File

@ -1,9 +1,9 @@
//! GPUState routines for drawing items in a systemsim //! GPUState routines for drawing items in a systemsim
use bytemuck; use bytemuck;
use cgmath::{EuclideanSpace, InnerSpace, Point2, Point3, Rad, Vector2}; use galactica_system::data::ShipState;
use galactica_system::{data::ShipState, phys::util}; use galactica_util::{constants::OBJECT_SPRITE_INSTANCE_LIMIT, to_radians};
use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT; use nalgebra::{Point2, Point3, Vector2};
use crate::{ use crate::{
globaluniform::ObjectData, globaluniform::ObjectData,
@ -29,14 +29,10 @@ impl GPUState {
ShipState::Collapsing { .. } | ShipState::Flying => { ShipState::Collapsing { .. } | ShipState::Flying => {
let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap(); let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap();
let pos = util::rigidbody_position(&r); let pos = *r.translation();
ship_pos = Point3 { ship_pos = Point3::new(pos.x, pos.y, 1.0);
x: pos.x, let ship_rot = r.rotation();
y: pos.y, ship_ang = ship_rot.angle();
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!
ship_cnt = state.ct.get_ship(ship.data.get_content()); ship_cnt = state.ct.get_ship(ship.data.get_content());
} }
ShipState::Landing { ShipState::Landing {
@ -48,14 +44,11 @@ impl GPUState {
} => { } => {
let target = state.ct.get_system_object(*target); let target = state.ct.get_system_object(*target);
let diff = Point2 { let diff = Point2::new(target.pos.x, target.pos.y) - from_position;
x: target.pos.x,
y: target.pos.y,
} - from_position;
ship_pos = ship.data.get_state().landing_position(state.ct).unwrap(); 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_ang = from_angle + ((target_angle - from_angle) * 1f32.min(elapsed / 1.0));
ship_cnt = state.ct.get_ship(ship.data.get_content()); ship_cnt = state.ct.get_ship(ship.data.get_content());
@ -65,7 +58,7 @@ impl GPUState {
to_angle, elapsed, .. to_angle, elapsed, ..
} => { } => {
ship_pos = ship.data.get_state().unlanding_position(state.ct).unwrap(); 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()); ship_cnt = state.ct.get_ship(ship.data.get_content());
} }
} }
@ -73,11 +66,8 @@ impl GPUState {
// Position adjusted for parallax // Position adjusted for parallax
// TODO: adjust parallax for zoom? // TODO: adjust parallax for zoom?
// 1.0 is z-coordinate, which is constant for ships // 1.0 is z-coordinate, which is constant for ships
let pos: Point2<f32> = (Point2 { let pos: Point2<f32> =
x: ship_pos.x, (Point2::new(ship_pos.x, ship_pos.y) - state.camera_pos) / ship_pos.z;
y: ship_pos.y,
} - state.camera_pos.to_vec())
/ ship_pos.z;
// Game dimensions of this sprite post-scale. // Game dimensions of this sprite post-scale.
// Post-scale width or height, whichever is larger. // Post-scale width or height, whichever is larger.
@ -104,7 +94,7 @@ impl GPUState {
xpos: ship_pos.x, xpos: ship_pos.x,
ypos: ship_pos.y, ypos: ship_pos.y,
zpos: ship_pos.z, zpos: ship_pos.z,
angle: ship_ang.0, angle: ship_ang,
size: ship_cnt.size, size: ship_cnt.size,
parent: 0, parent: 0,
is_child: 0, is_child: 0,
@ -142,10 +132,17 @@ impl GPUState {
&self.state.global_uniform.object_buffer, &self.state.global_uniform.object_buffer,
ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64, ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64,
bytemuck::cast_slice(&[ObjectData { 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, xpos: engine_point.pos.x,
ypos: engine_point.pos.y - engine_point.size / 2.0, ypos: engine_point.pos.y - engine_point.size / 2.0,
zpos: 1.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, size: engine_point.size,
parent: idx as u32, parent: idx as u32,
is_child: 1, is_child: 1,
@ -183,15 +180,15 @@ impl GPUState {
) { ) {
for p in state.systemsim.iter_projectiles() { for p in state.systemsim.iter_projectiles() {
let r = state.systemsim.get_rigid_body(p.rigid_body).unwrap(); let r = state.systemsim.get_rigid_body(p.rigid_body).unwrap();
let proj_pos = util::rigidbody_position(&r); let proj_pos = *r.translation();
let proj_rot = util::rigidbody_rotation(r); let proj_rot = r.rotation();
let proj_ang = -proj_rot.angle(Vector2 { x: 1.0, y: 0.0 }); let proj_ang = proj_rot.angle();
let proj_cnt = &p.content; // TODO: don't clone this? let proj_cnt = &p.content; // TODO: don't clone this?
// Position adjusted for parallax // Position adjusted for parallax
// TODO: adjust parallax for zoom? // TODO: adjust parallax for zoom?
// 1.0 is z-coordinate, which is constant for ships // 1.0 is z-coordinate, which is constant for projectiles
let pos: Point2<f32> = (proj_pos - state.camera_pos.to_vec()) / 1.0; let pos = (proj_pos - state.camera_pos) / 1.0;
// Game dimensions of this sprite post-scale. // Game dimensions of this sprite post-scale.
// Post-scale width or height, whichever is larger. // Post-scale width or height, whichever is larger.
@ -218,7 +215,7 @@ impl GPUState {
xpos: proj_pos.x, xpos: proj_pos.x,
ypos: proj_pos.y, ypos: proj_pos.y,
zpos: 1.0, zpos: 1.0,
angle: proj_ang.0, angle: proj_ang,
size: 0f32.max(proj_cnt.size + p.size_rng), size: 0f32.max(proj_cnt.size + p.size_rng),
parent: 0, parent: 0,
is_child: 0, is_child: 0,
@ -255,11 +252,7 @@ impl GPUState {
for o in &system.objects { for o in &system.objects {
// Position adjusted for parallax // Position adjusted for parallax
let pos: Point2<f32> = (Point2 { let pos: Point2<f32> = (Point2::new(o.pos.x, o.pos.y) - state.camera_pos) / o.pos.z;
x: o.pos.x,
y: o.pos.y,
} - state.camera_pos.to_vec())
/ o.pos.z;
// Game dimensions of this sprite post-scale. // Game dimensions of this sprite post-scale.
// Post-scale width or height, whichever is larger. // Post-scale width or height, whichever is larger.
@ -286,7 +279,7 @@ impl GPUState {
xpos: o.pos.x, xpos: o.pos.x,
ypos: o.pos.y, ypos: o.pos.y,
zpos: o.pos.z, zpos: o.pos.z,
angle: o.angle.0, angle: o.angle,
size: o.size, size: o.size,
parent: 0, parent: 0,
is_child: 0, is_child: 0,

View File

@ -1,8 +1,8 @@
use anyhow::Result; use anyhow::Result;
use bytemuck; use bytemuck;
use cgmath::Point2;
use galactica_util::constants::PARTICLE_SPRITE_INSTANCE_LIMIT; use galactica_util::constants::PARTICLE_SPRITE_INSTANCE_LIMIT;
use glyphon::Resolution; use glyphon::Resolution;
use nalgebra::Point2;
use std::iter; use std::iter;
use wgpu; use wgpu;
@ -87,8 +87,8 @@ impl super::GPUState {
bytemuck::cast_slice(&[ParticleInstance { bytemuck::cast_slice(&[ParticleInstance {
position: [i.pos.x, i.pos.y], position: [i.pos.x, i.pos.y],
velocity: i.velocity.into(), velocity: i.velocity.into(),
angle: i.angle.0, angle: i.angle,
angvel: i.angvel.0, angvel: i.angvel,
size: i.size, size: i.size,
sprite_index: i.sprite.get_index(), sprite_index: i.sprite.get_index(),
created: input.current_time, created: input.current_time,
@ -106,8 +106,8 @@ impl super::GPUState {
// Game coordinates (relative to camera) of ne and sw corners of screen. // Game coordinates (relative to camera) of ne and sw corners of screen.
// Used to skip off-screen sprites. // Used to skip off-screen sprites.
let clip_ne = 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::from((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 // 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) // (which we don't yet draw)

View File

@ -21,8 +21,7 @@ mod vertexbuffer;
pub use anchoredposition::PositionAnchor; pub use anchoredposition::PositionAnchor;
pub use datastructs::RenderInput; pub use datastructs::RenderInput;
pub use gpustate::GPUState; pub use gpustate::GPUState;
use nalgebra::Matrix4;
use cgmath::Matrix4;
/// Shader entry points /// Shader entry points
pub(crate) const SHADER_MAIN_VERTEX: &'static str = "vertex_main"; pub(crate) const SHADER_MAIN_VERTEX: &'static str = "vertex_main";

View File

@ -1,5 +1,5 @@
use cgmath::{Point2, Point3, Vector2, Vector3};
use galactica_content::Content; use galactica_content::Content;
use nalgebra::{Point2, Point3, Vector2, Vector3};
use rand::{self, Rng}; use rand::{self, Rng};
use crate::{ use crate::{
@ -40,20 +40,17 @@ impl Starfield {
let sz = ct.get_config().starfield_size as f32 / 2.0; let sz = ct.get_config().starfield_size as f32 / 2.0;
self.stars = (0..ct.get_config().starfield_count) self.stars = (0..ct.get_config().starfield_count)
.map(|_| StarfieldStar { .map(|_| StarfieldStar {
pos: Point3 { pos: Point3::new(
x: rng.gen_range(-sz..=sz), rng.gen_range(-sz..=sz),
y: rng.gen_range(-sz..=sz), rng.gen_range(-sz..=sz),
z: rng.gen_range( rng.gen_range(
ct.get_config().starfield_min_dist..=ct.get_config().starfield_max_dist, ct.get_config().starfield_min_dist..=ct.get_config().starfield_max_dist,
), ),
}, ),
size: rng.gen_range( size: rng.gen_range(
ct.get_config().starfield_min_size..ct.get_config().starfield_max_size, ct.get_config().starfield_min_size..ct.get_config().starfield_max_size,
), ),
tint: Vector2 { tint: Vector2::new(rng.gen_range(0.0..=1.0), rng.gen_range(0.0..=1.0)),
x: rng.gen_range(0.0..=1.0),
y: rng.gen_range(0.0..=1.0),
},
}) })
.collect(); .collect();
} }
@ -62,52 +59,48 @@ impl Starfield {
let sz = ct.get_config().starfield_size as f32; let sz = ct.get_config().starfield_size as f32;
// Compute window size in starfield tiles // Compute window size in starfield tiles
let mut nw_tile: Point2<i32> = { let mut nw_tile = {
// Game coordinates (relative to camera) of nw corner of screen. // 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. // Parallax correction.
// Also, adjust v for mod to work properly // Also, adjust v for mod to work properly
// (v is centered at 0) // (v is centered at 0)
let v: Point2<f32> = clip_nw * ct.get_config().starfield_min_dist; let v: Point2<f32> = clip_nw * ct.get_config().starfield_min_dist;
let v_adj: Point2<f32> = (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] #[rustfmt::skip]
// Compute m = fmod(x, sz) // Compute m = fmod(x, sz)
let m: Vector2<f32> = ( let m = Vector2::new(
(v_adj.x - (v_adj.x / sz).floor() * sz) - (sz / 2.0), (v_adj.x - (v_adj.x / sz).floor() * sz) - (sz / 2.0),
(v_adj.y - (v_adj.y / 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 // Now, remainder and convert to "relative tile" coordinates
// ( where (0,0) is center tile, (0, 1) is north, etc) // ( where (0,0) is center tile, (0, 1) is north, etc)
let rel = (v - m) / sz; let rel = (v - m) / sz;
// relative coordinates of north-east tile // 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, // We need to cover the window with stars,
// but we also need a one-wide buffer to account for motion. // 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 // Truncate tile grid to buffer size
// (The window won't be full of stars if our instance limit is too small) // (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) 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 > 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 // Add all tiles to buffer
self.instance_count = 0; // Keep track of buffer index self.instance_count = 0; // Keep track of buffer index
for x in (-nw_tile.x)..=nw_tile.x { for x in (-nw_tile.x)..=nw_tile.x {
for y in (-nw_tile.y)..=nw_tile.y { for y in (-nw_tile.y)..=nw_tile.y {
let offset = Vector3 { let offset = Vector3::new(sz * x as f32, sz * y as f32, 0.0);
x: sz * x as f32,
y: sz * y as f32,
z: 0.0,
};
for s in &self.stars { for s in &self.stars {
state.queue.write_buffer( state.queue.write_buffer(
&state.vertex_buffers.starfield.instances, &state.vertex_buffers.starfield.instances,

View File

@ -1,6 +1,6 @@
use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2}; use galactica_system::data::ShipState;
use galactica_system::{data::ShipState, phys::util}; use galactica_util::{constants::UI_SPRITE_INSTANCE_LIMIT, to_radians};
use galactica_util::constants::UI_SPRITE_INSTANCE_LIMIT; use nalgebra::{Point2, Vector2};
use crate::{ use crate::{
datastructs::RenderState, datastructs::RenderState,
@ -15,7 +15,7 @@ pub(super) struct Radar {
impl Radar { impl Radar {
pub fn new() -> Self { pub fn new() -> Self {
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() .get_state()
.landing_position(&input.ct) .landing_position(&input.ct)
.unwrap(); .unwrap();
self.last_player_position = Point2 { x: pos.x, y: pos.y } self.last_player_position = Point2::new(pos.x, pos.y)
} }
ShipState::UnLanding { .. } => { ShipState::UnLanding { .. } => {
@ -56,15 +56,12 @@ impl Radar {
.get_state() .get_state()
.unlanding_position(&input.ct) .unlanding_position(&input.ct)
.unwrap(); .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 } => { ShipState::Landed { target } => {
let landed_body = input.ct.get_system_object(*target); let landed_body = input.ct.get_system_object(*target);
self.last_player_position = Point2 { self.last_player_position = Point2::new(landed_body.pos.x, landed_body.pos.y);
x: landed_body.pos.x,
y: landed_body.pos.y,
};
} }
ShipState::Flying | ShipState::Collapsing { .. } => { ShipState::Flying | ShipState::Collapsing { .. } => {
let player_body = input let player_body = input
@ -72,7 +69,7 @@ impl Radar {
.get_rigid_body(player_ship.rigid_body) .get_rigid_body(player_ship.rigid_body)
.unwrap(); .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); let system = input.ct.get_system(input.current_system);
for o in &system.objects { for o in &system.objects {
let size = (o.size / o.pos.z) / (radar_range * system_object_scale); let size = (o.size / o.pos.z) / (radar_range * system_object_scale);
let p = Point2 { let p = Point2::new(o.pos.x, o.pos.y);
x: o.pos.x,
y: o.pos.y,
};
let d = (p - self.last_player_position) / radar_range; let d = (p - self.last_player_position) / radar_range;
// Add half the blip sprite's height to distance // Add half the blip sprite's height to distance
let m = d.magnitude() + (size / (2.0 * radar_size)); let m = d.magnitude() + (size / (2.0 * radar_size));
@ -133,12 +127,10 @@ impl Radar {
UiInstance::SIZE * state.vertex_buffers.ui_counter, UiInstance::SIZE * state.vertex_buffers.ui_counter,
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
position: (Point2 { position: (Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
x: radar_size / 2.0 + 10.0, + (d * (radar_size / 2.0)))
y: radar_size / -2.0 - 10.0,
} + (d * (radar_size / 2.0)))
.into(), .into(),
angle: o.angle.0, angle: o.angle,
size, size,
color: [0.5, 0.5, 0.5, 1.0], color: [0.5, 0.5, 0.5, 1.0],
sprite_index: planet_sprite.get_index(), sprite_index: planet_sprite.get_index(),
@ -171,7 +163,7 @@ impl Radar {
}; };
let ship = input.ct.get_ship(s.data.get_content()); let ship = input.ct.get_ship(s.data.get_content());
let size = (ship.size * ship.sprite.aspect) * ship_scale; let size = (ship.size * ship.sprite.aspect) * ship_scale;
let p = util::rigidbody_position(r); let p: Point2<f32> = (*r.translation()).into();
let d = (p - self.last_player_position) / radar_range; let d = (p - self.last_player_position) / radar_range;
let m = d.magnitude() + (size / (2.0 * radar_size)); let m = d.magnitude() + (size / (2.0 * radar_size));
if m < hide_range { if m < hide_range {
@ -179,12 +171,10 @@ impl Radar {
if size < 2.0 { if size < 2.0 {
continue; continue;
} }
let angle = util::rigidbody_rotation(r).angle(Vector2 { x: 0.0, y: 1.0 }); let angle = r.rotation().angle();
let position = Point2 { let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
x: radar_size / 2.0 + 10.0, + (d * (radar_size / 2.0));
y: radar_size / -2.0 - 10.0,
} + (d * (radar_size / 2.0));
// Enforce buffer limit // Enforce buffer limit
// TODO: cleaner solution. don't do this everywhere. // TODO: cleaner solution. don't do this everywhere.
@ -200,7 +190,7 @@ impl Radar {
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
position: position.into(), position: position.into(),
angle: -angle.0, // TODO: consistent angles angle: -angle, // TODO: consistent angles
size, size,
color, color,
sprite_index: ship_sprite.get_index(), sprite_index: ship_sprite.get_index(),
@ -211,10 +201,10 @@ impl Radar {
} }
// Draw viewport frame // Draw viewport frame
let d = Vector2 { let d = Vector2::new(
x: (input.camera_zoom / 2.0) * state.window_aspect, (input.camera_zoom / 2.0) * state.window_aspect,
y: input.camera_zoom / 2.0, input.camera_zoom / 2.0,
} / radar_range; ) / radar_range;
let m = d.magnitude(); let m = d.magnitude();
let d = d * (radar_size / 2.0); let d = d * (radar_size / 2.0);
let color = [0.3, 0.3, 0.3, 1.0]; let color = [0.3, 0.3, 0.3, 1.0];
@ -233,10 +223,10 @@ impl Radar {
UiInstance::SIZE * state.vertex_buffers.ui_counter, UiInstance::SIZE * state.vertex_buffers.ui_counter,
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwNw.to_int(), anchor: PositionAnchor::NwNw.to_int(),
position: Point2 { position: Point2::new(
x: (radar_size / 2.0 + 10.0) - d.x, (radar_size / 2.0 + 10.0) - d.x,
y: (radar_size / -2.0 - 10.0) + d.y, (radar_size / -2.0 - 10.0) + d.y,
} )
.into(), .into(),
angle: 0.0, angle: 0.0,
size, size,
@ -251,12 +241,12 @@ impl Radar {
UiInstance::SIZE * state.vertex_buffers.ui_counter, UiInstance::SIZE * state.vertex_buffers.ui_counter,
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwSw.to_int(), anchor: PositionAnchor::NwSw.to_int(),
position: Point2 { position: Point2::new(
x: (radar_size / 2.0 + 10.0) - d.x, (radar_size / 2.0 + 10.0) - d.x,
y: (radar_size / -2.0 - 10.0) - d.y, (radar_size / -2.0 - 10.0) - d.y,
} )
.into(), .into(),
angle: Rad::from(Deg(90.0)).0, angle: to_radians(90.0),
size, size,
color, color,
sprite_index: sprite.get_index(), sprite_index: sprite.get_index(),
@ -269,12 +259,12 @@ impl Radar {
UiInstance::SIZE * state.vertex_buffers.ui_counter, UiInstance::SIZE * state.vertex_buffers.ui_counter,
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwSe.to_int(), anchor: PositionAnchor::NwSe.to_int(),
position: Point2 { position: Point2::new(
x: (radar_size / 2.0 + 10.0) + d.x, (radar_size / 2.0 + 10.0) + d.x,
y: (radar_size / -2.0 - 10.0) - d.y, (radar_size / -2.0 - 10.0) - d.y,
} )
.into(), .into(),
angle: Rad::from(Deg(180.0)).0, angle: to_radians(180.0),
size, size,
color, color,
sprite_index: sprite.get_index(), sprite_index: sprite.get_index(),
@ -287,12 +277,12 @@ impl Radar {
UiInstance::SIZE * state.vertex_buffers.ui_counter, UiInstance::SIZE * state.vertex_buffers.ui_counter,
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwNe.to_int(), anchor: PositionAnchor::NwNe.to_int(),
position: Point2 { position: Point2::new(
x: (radar_size / 2.0 + 10.0) + d.x, (radar_size / 2.0 + 10.0) + d.x,
y: (radar_size / -2.0 - 10.0) + d.y, (radar_size / -2.0 - 10.0) + d.y,
} )
.into(), .into(),
angle: Rad::from(Deg(270.0)).0, angle: to_radians(270.0),
size, size,
color, color,
sprite_index: sprite.get_index(), sprite_index: sprite.get_index(),
@ -302,15 +292,14 @@ impl Radar {
} }
// Arrow to center of system // 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(); let m = q.magnitude();
if m > 200.0 { 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<f32> = Point2 { let position: Point2<f32> =
x: radar_size / 2.0 + 10.0, Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
y: radar_size / -2.0 - 10.0, + ((q.normalize() * 0.865) * (radar_size / 2.0));
} + ((q.normalize() * 0.865) * (radar_size / 2.0));
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT { if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better. // TODO: no panic, handle this better.
@ -323,7 +312,7 @@ impl Radar {
bytemuck::cast_slice(&[UiInstance { bytemuck::cast_slice(&[UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
position: position.into(), position: position.into(),
angle: -player_angle.0, angle: -player_angle,
size: 10.0, size: 10.0,
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)], color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
sprite_index: arrow_sprite.get_index(), sprite_index: arrow_sprite.get_index(),

View File

@ -24,5 +24,4 @@ galactica-playeragent = { workspace = true }
rapier2d = { workspace = true } rapier2d = { workspace = true }
nalgebra = { workspace = true } nalgebra = { workspace = true }
crossbeam = { workspace = true } crossbeam = { workspace = true }
cgmath = { workspace = true }
rand = { workspace = true } rand = { workspace = true }

View File

@ -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 std::{collections::HashMap, time::Instant};
use super::{OutfitSet, ShipPersonality}; 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. /// Ship state machine.
/// Any ship we keep track of is in one of these states. /// 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 /// The point, in world coordinates, where we started
from_position: Point2<f32>, from_position: Point2<f32>,
/// The ship's angle when we started landing /// The ship's angle when we started landing, in radians
from_angle: Rad<f32>, from_angle: f32,
/// The planet we're landing on /// The planet we're landing on
target: SystemObjectHandle, target: SystemObjectHandle,
@ -57,8 +57,8 @@ pub enum ShipState {
/// The point, in world coordinates, to which we're going /// The point, in world coordinates, to which we're going
to_position: Point2<f32>, to_position: Point2<f32>,
/// The angle we'll be at when we arrive /// The angle we'll be at when we arrive, in radians
to_angle: Rad<f32>, to_angle: f32,
/// The planet we're taking off from /// The planet we're taking off from
from: SystemObjectHandle, from: SystemObjectHandle,
@ -104,10 +104,7 @@ impl ShipState {
} => Some({ } => Some({
let target = ct.get_system_object(*target); let target = ct.get_system_object(*target);
let diff = Point2 { let diff = Point2::new(target.pos.x, target.pos.y) - from_position;
x: target.pos.x,
y: target.pos.y,
} - from_position;
let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8; let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8;
// TODO: improve animation // TODO: improve animation
@ -120,11 +117,11 @@ impl ShipState {
let pos = from_position + (diff * (elapsed / total)); let pos = from_position + (diff * (elapsed / total));
Point3 { Point3::new(
x: pos.x, pos.x,
y: pos.y, pos.y,
z: 1.0 + ((target.pos.z - 1.0) * (elapsed / total)), 1.0 + ((target.pos.z - 1.0) * (elapsed / total)),
} )
}), }),
_ => None, _ => None,
} }
@ -142,11 +139,7 @@ impl ShipState {
} => Some({ } => Some({
let from = ct.get_system_object(*from); let from = ct.get_system_object(*from);
let diff = to_position let diff = to_position - Point2::new(from.pos.x, from.pos.y);
- Point2 {
x: from.pos.x,
y: from.pos.y,
};
//let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8; //let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8;
// TODO: improve animation // TODO: improve animation
@ -157,16 +150,13 @@ impl ShipState {
// TODO: time by distance // TODO: time by distance
// TODO: keep momentum // TODO: keep momentum
let pos = Point2 { let pos = Point2::new(from.pos.x, from.pos.y) + (diff * (elapsed / total));
x: from.pos.x,
y: from.pos.y,
} + (diff * (elapsed / total));
Point3 { Point3::new(
x: pos.x, pos.x,
y: pos.y, pos.y,
z: from.pos.z + ((1.0 - from.pos.z) * (elapsed / total)), from.pos.z + ((1.0 - from.pos.z) * (elapsed / total)),
} )
}), }),
_ => None, _ => None,
} }
@ -232,7 +222,7 @@ impl ShipData {
&mut self, &mut self,
target: SystemObjectHandle, target: SystemObjectHandle,
from_position: Point2<f32>, from_position: Point2<f32>,
from_angle: Rad<f32>, from_angle: f32,
) -> bool { ) -> bool {
match self.state { match self.state {
ShipState::Flying => { ShipState::Flying => {
@ -257,7 +247,7 @@ impl ShipData {
ShipState::Landed { target } => { ShipState::Landed { target } => {
self.state = ShipState::UnLanding { self.state = ShipState::UnLanding {
to_position, to_position,
to_angle: Rad(1.0), to_angle: 1.0,
from: target, from: target,
total: 5.0, total: 5.0,
elapsed: 0.0, elapsed: 0.0,

View File

@ -1,5 +1,5 @@
use cgmath::{Deg, InnerSpace};
use galactica_content::Relationship; use galactica_content::Relationship;
use nalgebra::{Rotation2, Vector2};
use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle}; use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle};
use std::collections::HashMap; use std::collections::HashMap;
@ -8,7 +8,7 @@ use crate::data::ShipState;
use super::{ use super::{
super::{ super::{
objects::{PhysSimShip, ShipControls}, objects::{PhysSimShip, ShipControls},
util, PhysStepResources, PhysStepResources,
}, },
ShipControllerStruct, ShipControllerStruct,
}; };
@ -37,8 +37,8 @@ impl ShipControllerStruct for PointShipController {
let my_ship = ships.get(&this_ship).unwrap(); let my_ship = ships.get(&this_ship).unwrap();
let this_rigidbody = rigid_bodies.get(my_ship.rigid_body).unwrap(); let this_rigidbody = rigid_bodies.get(my_ship.rigid_body).unwrap();
let my_position = util::rigidbody_position(this_rigidbody); let my_position = this_rigidbody.translation();
let my_rotation = util::rigidbody_rotation(this_rigidbody); let my_rotation = this_rigidbody.rotation();
let my_angvel = this_rigidbody.angvel(); let my_angvel = this_rigidbody.angvel();
let my_faction = res.ct.get_faction(my_ship.data.get_faction()); let my_faction = res.ct.get_faction(my_ship.data.get_faction());
@ -59,13 +59,13 @@ impl ShipControllerStruct for PointShipController {
// Find the closest target // Find the closest target
let mut closest_enemy_position = match hostile_ships.next() { 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 None => return Some(controls), // Do nothing if no targets are available
}; };
let mut d = (my_position - closest_enemy_position).magnitude(); let mut d = (my_position - closest_enemy_position).magnitude();
for r in hostile_ships { for r in hostile_ships {
let p = util::rigidbody_position(r); let p = r.translation();
let new_d = (my_position - p).magnitude(); let new_d = (my_position - p).magnitude();
if new_d < d { if new_d < d {
d = new_d; d = new_d;
@ -73,13 +73,12 @@ impl ShipControllerStruct for PointShipController {
} }
} }
let angle_delta: Deg<f32> = (my_rotation) let angle = (closest_enemy_position - my_position).angle(&Vector2::new(1.0, 0.0));
.angle(closest_enemy_position - my_position) let angle_delta = my_rotation.angle_to(&Rotation2::new(angle).into());
.into();
if angle_delta < Deg(0.0) && my_angvel > -0.3 { if angle_delta < 0.0 && my_angvel > -0.3 {
controls.right = true; 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; controls.left = true;
} }

View File

@ -5,7 +5,6 @@ pub mod objects;
mod particlebuilder; mod particlebuilder;
mod stepresources; mod stepresources;
mod systemsim; mod systemsim;
pub mod util;
mod wrapper; mod wrapper;
pub use particlebuilder::*; pub use particlebuilder::*;

View File

@ -1,10 +1,9 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
use galactica_content::{CollapseEvent, Ship}; use galactica_content::{CollapseEvent, Ship};
use nalgebra::point; use nalgebra::{Point2, Vector2};
use rand::{rngs::ThreadRng, Rng}; use rand::{rngs::ThreadRng, Rng};
use rapier2d::{dynamics::RigidBody, geometry::Collider}; use rapier2d::{dynamics::RigidBody, geometry::Collider};
use super::super::{util, ParticleBuilder, PhysStepResources}; use super::super::{ParticleBuilder, PhysStepResources};
use crate::data::ShipData; use crate::data::ShipData;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -32,9 +31,9 @@ impl ShipCollapseSequence {
while !a { while !a {
y = self.rng.gen_range(-1.0..=1.0) * ship.size / 2.0; 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; 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 /// Step this sequence `t` seconds
@ -46,9 +45,8 @@ impl ShipCollapseSequence {
collider: &mut Collider, collider: &mut Collider,
) { ) {
let ship_content = res.ct.get_ship(ship_data.get_content()); let ship_content = res.ct.get_ship(ship_data.get_content());
let ship_pos = util::rigidbody_position(rigid_body); let ship_pos = rigid_body.translation();
let ship_rot = util::rigidbody_rotation(rigid_body); let ship_rot = rigid_body.rotation();
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 });
let (total, elapsed) = ship_data.get_state().collapse_state().unwrap(); let (total, elapsed) = ship_data.get_state().collapse_state().unwrap();
// How much time has passed since step() was last called // How much time has passed since step() was last called
@ -71,25 +69,22 @@ impl ShipCollapseSequence {
let effect = res.ct.get_effect(spawner.effect); let effect = res.ct.get_effect(spawner.effect);
for _ in 0..spawner.count as usize { for _ in 0..spawner.count as usize {
let pos = if let Some(pos) = spawner.pos { let pos: Vector2<f32> = if let Some(pos) = spawner.pos {
pos.to_vec() Vector2::new(pos.x, pos.y)
} else { } else {
self.random_in_ship(ship_content, collider) self.random_in_ship(ship_content, collider)
}; };
let pos = ship_pos let pos = ship_pos + (ship_rot * pos);
+ (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * 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( res.particles.push(ParticleBuilder::from_content(
effect, effect,
pos, pos.into(),
Rad::zero(), 0.0,
Vector2 { velocity,
x: velocity.x, Vector2::new(0.0, 0.0),
y: velocity.y,
},
Vector2::zero(),
)) ))
} }
} }
@ -121,21 +116,21 @@ impl ShipCollapseSequence {
if self.rng.gen_range(0.0..=1.0) <= p_add { if self.rng.gen_range(0.0..=1.0) <= p_add {
let pos = if let Some(pos) = spawner.pos { let pos = if let Some(pos) = spawner.pos {
pos.to_vec() Vector2::new(pos.x, pos.y)
} else { } else {
self.random_in_ship(ship_content, collider) self.random_in_ship(ship_content, collider)
}; };
// Position, adjusted for ship rotation // Position, adjusted for ship rotation
let pos = ship_pos + Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos; let pos = ship_pos + (ship_rot * pos);
let vel = rigid_body.velocity_at_point(&point![pos.x, pos.y]); let vel = rigid_body.velocity_at_point(&Point2::new(pos.x, pos.y));
res.particles.push(ParticleBuilder { res.particles.push(ParticleBuilder {
sprite: effect.sprite, sprite: effect.sprite,
pos, pos: pos.into(),
velocity: Vector2 { x: vel.x, y: vel.y }, velocity: vel,
angle: Rad::zero(), angle: 0.0,
angvel: Rad::zero(), angvel: 0.0,
lifetime: effect.lifetime, lifetime: effect.lifetime,
size: effect.size, size: effect.size,
fade: 0.0, fade: 0.0,

View File

@ -1,6 +1,5 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2, Zero};
use galactica_content::{Content, FactionHandle, ShipHandle}; use galactica_content::{Content, FactionHandle, ShipHandle};
use nalgebra::{point, vector}; use nalgebra::{point, vector, Rotation2, Vector2};
use rand::Rng; use rand::Rng;
use rapier2d::{ use rapier2d::{
dynamics::{RigidBody, RigidBodyHandle}, dynamics::{RigidBody, RigidBodyHandle},
@ -10,7 +9,7 @@ use rapier2d::{
use crate::data::{ShipData, ShipPersonality, ShipState}; use crate::data::{ShipData, ShipPersonality, ShipState};
use super::{ use super::{
super::{util, ParticleBuilder, PhysStepResources}, super::{ParticleBuilder, PhysStepResources},
collapse::ShipCollapseSequence, collapse::ShipCollapseSequence,
}; };
@ -114,9 +113,9 @@ impl PhysSimShip {
collider: &mut Collider, collider: &mut Collider,
) { ) {
let ship_content = res.ct.get_ship(self.data.get_content()); let ship_content = res.ct.get_ship(self.data.get_content());
let ship_pos = util::rigidbody_position(&rigid_body); let ship_pos = rigid_body.translation();
let ship_rot = util::rigidbody_rotation(rigid_body); let ship_rot = rigid_body.rotation();
let ship_ang = ship_rot.angle(Vector2 { x: 1.0, y: 0.0 }); let ship_ang = ship_rot.angle();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
if self.data.get_hull() <= ship_content.damage.hull { if self.data.get_hull() <= ship_content.damage.hull {
@ -125,7 +124,7 @@ impl PhysSimShip {
let effect = res.ct.get_effect(e.effect); let effect = res.ct.get_effect(e.effect);
let pos = if let Some(pos) = e.pos { let pos = if let Some(pos) = e.pos {
pos.to_vec() Vector2::new(pos.x, pos.y)
} else { } else {
// Pick a random point inside this ship's collider // Pick a random point inside this ship's collider
let mut y = 0.0; let mut y = 0.0;
@ -138,29 +137,24 @@ impl PhysSimShip {
/ 2.0; / 2.0;
a = collider.shape().contains_local_point(&point![x, y]); a = collider.shape().contains_local_point(&point![x, y]);
} }
Vector2 { x, y } Vector2::new(x, y)
}; };
let pos = let pos = ship_pos + (Rotation2::new(ship_ang) * pos);
ship_pos + (Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos);
let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]); let velocity = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
res.particles.push(ParticleBuilder::from_content( res.particles.push(ParticleBuilder::from_content(
effect, effect,
pos, pos.into(),
Rad::zero(), 0.0,
Vector2 { velocity,
x: velocity.x, Vector2::new(0.0, 0.0),
y: velocity.y,
},
Vector2::zero(),
)) ))
} }
} }
} }
let engine_force = ship_rot * res.t; let engine_force = ship_rot * (Vector2::new(1.0, 0.0) * res.t);
if self.controls.thrust { if self.controls.thrust {
rigid_body.apply_impulse( rigid_body.apply_impulse(

View File

@ -1,5 +1,5 @@
use cgmath::{Matrix2, Point2, Rad, Vector2};
use galactica_content::{Effect, SpriteHandle}; use galactica_content::{Effect, SpriteHandle};
use nalgebra::{Point2, Rotation2, Vector2};
use rand::Rng; use rand::Rng;
/// Instructions to create a new particle /// Instructions to create a new particle
@ -14,11 +14,11 @@ pub struct ParticleBuilder {
/// This particle's velocity, in world coordinates /// This particle's velocity, in world coordinates
pub velocity: Vector2<f32>, pub velocity: Vector2<f32>,
/// This particle's angle /// This particle's angle, in radians
pub angle: Rad<f32>, pub angle: f32,
/// This particle's angular velocity (rad/sec) /// This particle's angular velocity (rad/sec)
pub angvel: Rad<f32>, pub angvel: f32,
/// This particle's lifetime, in seconds /// This particle's lifetime, in seconds
pub lifetime: f32, pub lifetime: f32,
@ -36,7 +36,7 @@ impl ParticleBuilder {
pub fn from_content( pub fn from_content(
effect: &Effect, effect: &Effect,
pos: Point2<f32>, pos: Point2<f32>,
parent_angle: Rad<f32>, parent_angle: f32,
parent_velocity: Vector2<f32>, parent_velocity: Vector2<f32>,
target_velocity: Vector2<f32>, target_velocity: Vector2<f32>,
) -> Self { ) -> Self {
@ -51,21 +51,18 @@ impl ParticleBuilder {
let velocity = ((effect.velocity_scale_parent + a) * parent_velocity) let velocity = ((effect.velocity_scale_parent + a) * parent_velocity)
+ ((effect.velocity_scale_target + b) * target_velocity); + ((effect.velocity_scale_target + b) * target_velocity);
Matrix2::from_angle(Rad( Rotation2::new(rng.gen_range(-effect.direction_rng..=effect.direction_rng)) * velocity
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 { let angvel = if effect.angvel_rng == 0.0 {
effect.angvel effect.angvel
} else { } 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 { let angle = if effect.angle_rng == 0.0 {
parent_angle + effect.angle parent_angle + effect.angle
} else { } 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 { ParticleBuilder {

View File

@ -1,24 +1,23 @@
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2, Zero};
use galactica_content::{ use galactica_content::{
Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle, Content, FactionHandle, GunPoint, OutfitHandle, ProjectileCollider, Relationship, ShipHandle,
SystemHandle, SystemObjectHandle, SystemHandle, SystemObjectHandle,
}; };
use galactica_playeragent::PlayerAgent; use galactica_playeragent::PlayerAgent;
use nalgebra::{point, vector}; use nalgebra::{point, vector, Point2, Rotation2, Vector2};
use rand::Rng; use rand::Rng;
use rapier2d::{ use rapier2d::{
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet}, dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet},
geometry::{ColliderBuilder, ColliderHandle, ColliderSet}, geometry::{ColliderBuilder, ColliderHandle, ColliderSet},
pipeline::ActiveEvents, pipeline::ActiveEvents,
}; };
use std::{collections::HashMap, f32::consts::PI}; use std::collections::HashMap;
use crate::data::{ShipPersonality, ShipState}; use crate::data::{ShipPersonality, ShipState};
use super::{ use super::{
controller::ShipController, controller::ShipController,
objects::{PhysProjectile, PhysSimShip}, objects::{PhysProjectile, PhysSimShip},
util, ParticleBuilder, PhysStepResources, ParticleBuilder, PhysStepResources,
}; };
// TODO: replace with a more generic handle // TODO: replace with a more generic handle
@ -71,11 +70,8 @@ impl<'a> PhysSim {
let ship = self.ships.get_mut(&collider).unwrap(); let ship = self.ships.get_mut(&collider).unwrap();
let r = self.rigid_body_set.get(ship.rigid_body).unwrap(); let r = self.rigid_body_set.get(ship.rigid_body).unwrap();
ship.data.land_on( ship.data
target, .land_on(target, (*r.translation()).into(), r.rotation().angle());
util::rigidbody_position(r),
-util::rigidbody_rotation(r).angle(Vector2 { x: 0.0, y: 1.0 }),
);
let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap();
r.set_enabled(false); r.set_enabled(false);
@ -93,10 +89,7 @@ impl<'a> PhysSim {
let obj = ship.data.get_state().landed_on().unwrap(); let obj = ship.data.get_state().landed_on().unwrap();
let obj = ct.get_system_object(obj); let obj = ct.get_system_object(obj);
let target_pos = Point2 { let target_pos = Point2::new(obj.pos.x + 100.0, obj.pos.y + 100.0);
x: obj.pos.x + 100.0,
y: obj.pos.y + 100.0,
};
ship.data.unland(target_pos); ship.data.unland(target_pos);
@ -164,8 +157,9 @@ impl<'a> PhysSim {
if destory_projectile { if destory_projectile {
let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap();
let v = util::rigidbody_velocity(pr).normalize() * projectile.content.force; let v =
let pos = util::rigidbody_position(pr); pr.velocity_at_point(pr.center_of_mass()).normalize() * projectile.content.force;
let pos = *pr.translation();
let _ = pr; let _ = pr;
let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); 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 // Borrow again, we can only have one at a time
let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap(); let pr = self.rigid_body_set.get(projectile.rigid_body).unwrap();
let pos = util::rigidbody_position(pr); let pos = *pr.translation();
let angle = util::rigidbody_rotation(pr).angle(Vector2 { x: 1.0, y: 0.0 }); let angle = pr.rotation().angle();
match &projectile.content.impact_effect { match &projectile.content.impact_effect {
None => {} None => {}
@ -182,19 +176,16 @@ impl<'a> PhysSim {
let effect = res.ct.get_effect(*x); let effect = res.ct.get_effect(*x);
let r = ship.rigid_body; let r = ship.rigid_body;
let sr = self.get_rigid_body(r).unwrap(); 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 = let target_velocity =
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y)); sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
res.particles.push(ParticleBuilder::from_content( res.particles.push(ParticleBuilder::from_content(
effect, effect,
pos, pos.into(),
-angle, -angle,
parent_velocity, parent_velocity,
Vector2 { target_velocity,
x: target_velocity.x,
y: target_velocity.y,
},
)); ));
} }
}; };
@ -228,15 +219,8 @@ impl PhysSim {
position: Point2<f32>, position: Point2<f32>,
) -> PhysSimShipHandle { ) -> PhysSimShipHandle {
let ship_content = ct.get_ship(handle); let ship_content = ct.get_ship(handle);
let cl = ColliderBuilder::convex_decomposition( let cl = ship_content.collider.0.clone();
&ship_content.collision.points[..], // TODO: additonal ship mass from outfits and cargo
&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 rb = RigidBodyBuilder::dynamic() let rb = RigidBodyBuilder::dynamic()
.angular_damping(ship_content.angular_drag) .angular_damping(ship_content.angular_drag)
@ -247,7 +231,7 @@ impl PhysSim {
let ridid_body = self.rigid_body_set.insert(rb.build()); let ridid_body = self.rigid_body_set.insert(rb.build());
let collider = let collider =
self.collider_set 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( self.ship_behaviors.insert(
collider, collider,
@ -379,34 +363,31 @@ impl PhysSim {
let ship = self.ships.get(&collider).unwrap(); let ship = self.ships.get(&collider).unwrap();
let rigid_body = self.get_rigid_body(ship.rigid_body).unwrap(); let rigid_body = self.get_rigid_body(ship.rigid_body).unwrap();
let ship_pos = util::rigidbody_position(rigid_body); let ship_pos = rigid_body.translation();
let ship_rot = util::rigidbody_rotation(rigid_body); let ship_rot = rigid_body.rotation();
let ship_vel = util::rigidbody_velocity(rigid_body); let ship_ang = ship_rot.angle();
let ship_ang = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); 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 = res.ct.get_outfit(outfit);
let outfit = outfit.gun.as_ref().unwrap(); let outfit = outfit.gun.as_ref().unwrap();
let spread: Rad<f32> = let spread = rng.gen_range(-outfit.projectile.angle_rng..=outfit.projectile.angle_rng);
Deg(rng.gen_range(-outfit.projectile.angle_rng.0..=outfit.projectile.angle_rng.0))
.into();
let vel = ship_vel let vel = ship_vel
+ (Matrix2::from_angle(-ship_ang + spread) + (Rotation2::new(ship_ang + spread)
* Vector2 { * Vector2::new(
x: 0.0, outfit.projectile.speed
y: outfit.projectile.speed
+ rng.gen_range( + rng.gen_range(
-outfit.projectile.speed_rng..=outfit.projectile.speed_rng, -outfit.projectile.speed_rng..=outfit.projectile.speed_rng,
), ),
}); 0.0,
));
let rigid_body = RigidBodyBuilder::kinematic_velocity_based() let rigid_body = RigidBodyBuilder::kinematic_velocity_based()
.translation(vector![pos.x, pos.y]) .translation(vector![pos.x, pos.y])
.rotation(-ship_ang.0) .rotation(ship_ang)
.linvel(vector![vel.x, vel.y]) .linvel(vel)
.build(); .build();
let collider = match &outfit.projectile.collider { let collider = match &outfit.projectile.collider {
@ -487,9 +468,9 @@ impl PhysSim {
None => {} None => {}
Some(x) => { Some(x) => {
let x = res.ct.get_effect(*x); let x = res.ct.get_effect(*x);
let pos = util::rigidbody_position(&pr); let pos = *pr.translation();
let vel = util::rigidbody_velocity(&pr); let vel = pr.velocity_at_point(pr.center_of_mass());
let angle = util::rigidbody_rotation(&pr).angle(Vector2 { x: 1.0, y: 0.0 }); let angle = pr.rotation().angle();
let velocity = { let velocity = {
let a = rng let a = rng
@ -502,10 +483,10 @@ impl PhysSim {
res.particles.push(ParticleBuilder::from_content( res.particles.push(ParticleBuilder::from_content(
x, x,
pos, pos.into(),
-angle, -angle,
velocity, velocity,
Vector2::zero(), Vector2::new(0.0, 0.0),
)); ));
} }
}; };

View File

@ -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<f32> {
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<f32> {
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<f32> {
let v = r.velocity_at_point(&nalgebra::Point2::new(
r.translation()[0],
r.translation()[1],
));
Vector2 { x: v.x, y: v.y }
}

View File

@ -4,3 +4,8 @@
pub mod constants; pub mod constants;
pub mod timing; 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;
}