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"
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",

View File

@ -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"

View File

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

View File

@ -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 }

View File

@ -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<f32>,
/// 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<f32>,
/// How fast this particle spins, in radians/sec
pub angvel: f32,
/// Random angvel variation
pub angvel_rng: f32,

View File

@ -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<f32>,
/// 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<EffectHandle>,

View File

@ -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<Point<f32, 2>>,
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<f32>,
pub pos: Vector2<f32>,
/// 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<f32>,
pub pos: Vector2<f32>,
}
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<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 {
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),
});
}

View File

@ -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<f32>,
/// This object's sprite's angle.
pub angle: Rad<f32>,
/// 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,

View File

@ -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<f32>,
/// The radius of this polar coordinate
pub radius: f32,
pub angle: Rad<f32>,
/// In radians
pub angle: f32,
}
impl Polar {
pub fn to_cartesian(self) -> Point2<f32> {
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;
}

View File

@ -32,4 +32,4 @@ winit = { workspace = true }
wgpu = { workspace = true }
pollster = { 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_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();

View File

@ -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,

View File

@ -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 }

View File

@ -1,9 +1,9 @@
use cgmath::Point2;
use nalgebra::Vector2;
#[derive(Debug, Clone, Copy)]
pub struct Camera {
/// Camera center
pub pos: Point2<f32>,
pub pos: Vector2<f32>,
/// 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,
}

View File

@ -1,5 +1,5 @@
use cgmath::Point2;
use nalgebra::Vector2;
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 }
anyhow = { workspace = true }
cgmath = { workspace = true }
nalgebra = { workspace = true }
rand = { workspace = true }
image = { 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
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<f32>, 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

View File

@ -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<f32>,
pub camera_pos: Vector2<f32>,
/// Player ship data
pub player: &'a PlayerAgent,

View File

@ -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<f32> = (Point2 {
x: ship_pos.x,
y: ship_pos.y,
} - state.camera_pos.to_vec())
/ ship_pos.z;
let pos: Point2<f32> =
(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<f32> = (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<f32> = (Point2 {
x: o.pos.x,
y: o.pos.y,
} - state.camera_pos.to_vec())
/ o.pos.z;
let pos: Point2<f32> = (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,

View File

@ -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)

View File

@ -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";

View File

@ -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<i32> = {
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<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]
// 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.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,

View File

@ -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<f32> = (*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<f32> = 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<f32> =
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(),

View File

@ -24,5 +24,4 @@ galactica-playeragent = { workspace = true }
rapier2d = { workspace = true }
nalgebra = { workspace = true }
crossbeam = { workspace = true }
cgmath = { 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 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<f32>,
/// The ship's angle when we started landing
from_angle: Rad<f32>,
/// 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<f32>,
/// The angle we'll be at when we arrive
to_angle: Rad<f32>,
/// 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<f32>,
from_angle: Rad<f32>,
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,

View File

@ -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<f32> = (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;
}

View File

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

View File

@ -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<f32> = 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,

View File

@ -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(

View File

@ -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<f32>,
/// This particle's angle
pub angle: Rad<f32>,
/// This particle's angle, in radians
pub angle: f32,
/// This particle's angular velocity (rad/sec)
pub angvel: Rad<f32>,
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<f32>,
parent_angle: Rad<f32>,
parent_angle: f32,
parent_velocity: Vector2<f32>,
target_velocity: Vector2<f32>,
) -> 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 {

View File

@ -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<f32>,
) -> 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<f32> =
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),
));
}
};

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 timing;
/// Convert an angle in degrees to radians
pub fn to_radians(degrees: f32) -> f32 {
return (degrees / 360.0) * std::f32::consts::TAU;
}