Compare commits

...

3 Commits

Author SHA1 Message Date
Mark 8d3e5e581a
Updated TODO 2024-01-23 21:21:36 -08:00
Mark d6a3e10a84
Added logger 2024-01-23 21:21:31 -08:00
Mark 98b460aba9
Added sticky particles 2024-01-23 21:21:15 -08:00
25 changed files with 559 additions and 173 deletions

244
Cargo.lock generated
View File

@ -75,6 +75,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@ -147,6 +153,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arc-swap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
[[package]]
name = "arrayref"
version = "0.3.7"
@ -315,6 +327,20 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "chrono"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.0",
]
[[package]]
name = "clap"
version = "4.4.18"
@ -535,6 +561,23 @@ dependencies = [
"winapi",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "destructure_traitobject"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
[[package]]
name = "dispatch"
version = "0.2.0"
@ -634,6 +677,12 @@ dependencies = [
"spin",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fontconfig-parser"
version = "0.5.3"
@ -724,6 +773,8 @@ dependencies = [
"galactica-system",
"galactica-util",
"image",
"log",
"log4rs",
"nalgebra",
"pollster",
"rand",
@ -741,6 +792,7 @@ dependencies = [
"galactica-util",
"image",
"lazy_static",
"log",
"nalgebra",
"rapier2d",
"serde",
@ -787,6 +839,7 @@ dependencies = [
"galactica-util",
"glyphon",
"image",
"log",
"nalgebra",
"rand",
"wgpu",
@ -927,7 +980,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
dependencies = [
"bitflags 2.4.1",
"gpu-descriptor-types",
"hashbrown",
"hashbrown 0.14.3",
]
[[package]]
@ -948,6 +1001,12 @@ dependencies = [
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.3"
@ -985,6 +1044,35 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iana-time-zone"
version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "image"
version = "0.24.7"
@ -1004,6 +1092,16 @@ dependencies = [
"tiff",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.1.0"
@ -1011,7 +1109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.14.3",
]
[[package]]
@ -1026,6 +1124,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jni-sys"
version = "0.3.0"
@ -1131,6 +1235,12 @@ dependencies = [
"redox_syscall 0.4.1",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
version = "0.4.11"
@ -1146,6 +1256,41 @@ name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
dependencies = [
"serde",
]
[[package]]
name = "log-mdc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
[[package]]
name = "log4rs"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd"
dependencies = [
"anyhow",
"arc-swap",
"chrono",
"derivative",
"fnv",
"humantime",
"libc",
"log",
"log-mdc",
"parking_lot",
"serde",
"serde-value",
"serde_json",
"serde_yaml",
"thiserror",
"thread-id",
"typemap-ors",
"winapi",
]
[[package]]
name = "lru"
@ -1153,7 +1298,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7"
dependencies = [
"hashbrown",
"hashbrown 0.14.3",
]
[[package]]
@ -1255,7 +1400,7 @@ dependencies = [
"bitflags 2.4.1",
"codespan-reporting",
"hexf-parse",
"indexmap",
"indexmap 2.1.0",
"log",
"num-traits",
"rustc-hash",
@ -1517,6 +1662,15 @@ dependencies = [
"libredox",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "owned_ttf_parser"
version = "0.20.0"
@ -1824,6 +1978,12 @@ dependencies = [
"unicode-script",
]
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "safe_arch"
version = "0.7.1"
@ -1882,6 +2042,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
@ -1893,6 +2063,17 @@ dependencies = [
"syn 2.0.42",
]
[[package]]
name = "serde_json"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
@ -1902,6 +2083,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
dependencies = [
"indexmap 1.9.3",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "simba"
version = "0.8.1"
@ -1970,7 +2163,7 @@ version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd774eb23cff002036706e6ea83c3f4ab4c80dad89da76fe16d49f77ab71682f"
dependencies = [
"hashbrown",
"hashbrown 0.14.3",
"num-traits",
"robust",
"smallvec",
@ -2089,6 +2282,16 @@ dependencies = [
"syn 2.0.42",
]
[[package]]
name = "thread-id"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "tiff"
version = "0.9.0"
@ -2167,7 +2370,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"indexmap 2.1.0",
"toml_datetime",
"winnow",
]
@ -2178,7 +2381,7 @@ version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap",
"indexmap 2.1.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -2197,6 +2400,15 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
[[package]]
name = "typemap-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867"
dependencies = [
"unsafe-any-ors",
]
[[package]]
name = "typenum"
version = "1.17.0"
@ -2263,6 +2475,15 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unsafe-any-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad"
dependencies = [
"destructure_traitobject",
]
[[package]]
name = "utf8parse"
version = "0.2.1"
@ -2891,6 +3112,15 @@ version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "yazi"
version = "0.1.6"

View File

@ -72,3 +72,5 @@ toml = "0.8.8"
glyphon = "0.4.1"
lazy_static = "1.4.0"
clap = { version = "4.4.18", features = ["derive"] }
log = "0.4.20"
log4rs = { version = "1.2.0", features = ["console_appender"] }

23
TODO.md
View File

@ -1,14 +1,12 @@
# Specific projects
## Currently working on:
- first: sticky effects
- first: read state from image so we can add objects?
- clickable buttons
- planet outfitter
## Small jobs
- 🌟 clean up and document short sprite sections
- 🌟 Better planet desc formatting
- Procedural suns
- 🌟 Back arrow -> reverse
@ -16,7 +14,7 @@
- 🌟 User-configurable outfit space types
- 🌟 Sticky radar
- Configurable radar
- 🌟 Ship damage events, unify spawners
- 🌟 Ship damage events
- Better landing animation (slow down)
- Land from farther away
- Ship collapse: damage + force events
@ -37,18 +35,15 @@
- Correct drawing order (player on top, landing ships)
- Faster handles (better than a hashmap?)
- Check for handle leaks
- 🌟 Log/warning system
- Better physshiphandle
- Clean up & faster frame timings (average)
- 🌟 Handle lost focus
- User config file
- 🌟 Document content
- 🌟 Clean up content: one ship per file, autodetect
- CLI: content location, logs, etc
- Better WGSL preprocessor?
- Projectile performance
- Starfield clusters, shader instead of an array?
- CLI: specify dirs, content location, logs, etc
- Starfield clusters, less flicker
- Collider groups for factions? (projectile optimization)
- Better error when sprite is missing from atlas
## 🌟 Player selection
- Planet name, ring, and distance
@ -115,12 +110,6 @@
- Cache direcory
- How to pack?
## Effect physics
- Effect should stick to their ships. How?
- Land and unland effects
- Effect on fire gun
- Effect / sprite color variation
## Sound effects
- Sound effects
- User-configurable music (?, game config or user config?)
@ -176,6 +165,9 @@
- Galaxy date system, slowly orbiting planets
- Parallelize frame computations
- Advanced AI: avoid collisions
- Land and unland effects
- Muzzle effect
- Effect / sprite color variation
## Game & Story
@ -217,6 +209,7 @@
- Outfits may not change unless you've landed. They might not change ever for CC ships!
- All angle adjustments happen in content & shaders
- Reserved texture: index zero
- Errors: lowercase, no punctuation
## Ideas

View File

@ -1,75 +1,71 @@
[effect."small explosion"]
sprite = "effect::explosion::small"
lifetime = "inherit"
inherit_velocity = "target"
size = 8.0
size_rng = 1.6
angle_rng = 360
velocity_scale_parent = 1.0
fade = 0.2
fade_rng = 0.1
velocity.sticky = "parent"
[effect."large explosion"]
sprite = "effect::explosion::large"
lifetime = "inherit"
inherit_velocity = "target"
size = 25.0
size_rng = 5.0
angle_rng = 360
velocity_scale_parent = 1.0
fade = 0.2
fade_rng = 0.1
velocity.sticky = "parent"
[effect."huge explosion"]
sprite = "effect::explosion::huge"
lifetime = "inherit"
inherit_velocity = "target"
size = 50.0
size_rng = 10.0
angle_rng = 360
velocity_scale_parent = 1.0
fade = 0.2
fade_rng = 0.1
velocity.sticky = "parent"
[effect."blue spark"]
sprite = "effect::spark::blue"
lifetime = 0.5
lifetime_rng = 0.5
inherit_velocity = "parent"
size = 4.0
size_rng = 2.0
angle_rng = 360
angvel_rng = 0.0
velocity_scale_parent = 1.0
fade = 0.2
fade_rng = 0.1
velocity.sticky = "parent"
[effect."yellow spark"]
sprite = "effect::spark::yellow"
lifetime = "inherit"
inherit_velocity = "parent"
size = 4.0
size_rng = 2.0
angle_rng = 360
angvel_rng = 0.0
velocity_scale_parent = 1.0
fade = 0.2
fade_rng = 0.1
velocity.sticky = "parent"
[effect."red spark"]
sprite = "effect::spark::red"
lifetime = "inherit"
inherit_velocity = "parent"
size = 4.0
size_rng = 1.0
angle_rng = 360
angvel_rng = 0.0
velocity_scale_parent = 1.0
fade = 0.2
fade_rng = 0.1
velocity.sticky = "parent"
# Every effect has a parent, some effects have a target
@ -90,12 +86,11 @@ angvel = 0.0 # Angular velocity at creation
# Total velocity is sum of parent + target velocities with scale applied
velocity_scale_parent = 0.0 # Multiply velocity by this value
velocity_scale_parent_rng = 0.0 # random variation of scale
velocity_scale_target = 1.0
velocity_scale_target_rng = 1.0
direction_rng = 0.0 # Random variation of travel direction, in degrees, applied to velocity vector (/2 each side?)
velocity.scale_parent = 0.0 # Multiply velocity by this value
velocity.scale_parent_rng = 0.0 # random variation of scale
velocity.scale_target = 1.0
velocity.scale_target_rng = 1.0
velocity.direction_rng = 0.0 # Random variation of travel direction, in degrees, applied to velocity vector (/2 each side?)
fade = 0.2
fade_rng = 0.1

View File

@ -54,4 +54,4 @@ gun.projectile.impact_effect = "blaster impact"
gun.projectile.expire_effect.sprite = "effect::blaster"
gun.projectile.expire_effect.lifetime = "inherit"
gun.projectile.expire_effect.size = 3.0
gun.projectile.expire_effect.velocity_scale_parent = 1.0
gun.projectile.expire_effect.velocity.scale_parent = 1.0

View File

@ -28,3 +28,4 @@ nalgebra = { workspace = true }
image = { workspace = true }
rapier2d = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }

View File

@ -10,6 +10,7 @@ mod util;
use anyhow::{bail, Context, Result};
use galactica_packer::{SpriteAtlas, SpriteAtlasImage};
use log::warn;
use std::{
collections::HashMap,
fs::File,
@ -179,22 +180,22 @@ impl Content {
match e.path().extension() {
Some(t) => {
if t.to_str() != Some("toml") {
println!("[WARNING] {e:#?} is not a toml file, skipping.");
warn!("{e:#?} is not a toml file, skipping");
continue;
}
}
None => {
println!("[WARNING] {e:#?} is not a toml file, skipping.");
warn!("{e:#?} is not a toml file, skipping");
continue;
}
}
let path = e.path();
let this_root = Self::try_parse(path)
.with_context(|| format!("Could not read {}", path.display()))?;
.with_context(|| format!("could not read {}", path.display()))?;
root.merge(this_root)
.with_context(|| format!("Could not parse {}", path.display()))?;
.with_context(|| format!("could not parse {}", path.display()))?;
}
}

View File

@ -23,13 +23,26 @@ pub(crate) mod syntax {
pub angle_rng: Option<f32>,
pub angvel: Option<f32>,
pub angvel_rng: Option<f32>,
pub velocity_scale_parent: Option<f32>,
pub velocity_scale_parent_rng: Option<f32>,
pub velocity_scale_target: Option<f32>,
pub velocity_scale_target_rng: Option<f32>,
pub direction_rng: Option<f32>,
pub fade: Option<f32>,
pub fade_rng: Option<f32>,
pub velocity: EffectVelocity,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum EffectVelocity {
Sticky {
sticky: String,
},
Explicit {
scale_parent: Option<f32>,
scale_parent_rng: Option<f32>,
scale_target: Option<f32>,
scale_target_rng: Option<f32>,
direction_rng: Option<f32>,
},
}
#[derive(Debug, Deserialize)]
@ -69,12 +82,38 @@ pub(crate) mod syntax {
}
};
let velocity = match self.velocity {
EffectVelocity::Explicit {
scale_parent,
scale_parent_rng,
scale_target,
scale_target_rng,
direction_rng,
} => super::EffectVelocity::Explicit {
scale_parent: scale_parent.unwrap_or(0.0),
scale_parent_rng: scale_parent_rng.unwrap_or(0.0),
scale_target: scale_target.unwrap_or(0.0),
scale_target_rng: scale_target_rng.unwrap_or(0.0),
direction_rng: direction_rng.unwrap_or(0.0) / 2.0,
},
EffectVelocity::Sticky { sticky } => {
if sticky == "parent" {
super::EffectVelocity::StickyParent
} else if sticky == "target" {
super::EffectVelocity::StickyTarget
} else {
bail!("bad sticky specification `{}`", sticky);
}
}
};
let handle = EffectHandle {
index: content.effects.len(),
};
content.effects.push(super::Effect {
handle,
sprite,
velocity,
size: self.size,
size_rng: self.size_rng.unwrap_or(0.0),
lifetime,
@ -83,11 +122,6 @@ pub(crate) mod syntax {
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),
velocity_scale_target_rng: self.velocity_scale_target_rng.unwrap_or(0.0),
direction_rng: self.direction_rng.unwrap_or(0.0) / 2.0,
fade: self.fade.unwrap_or(0.0),
fade_rng: self.fade_rng.unwrap_or(0.0),
});
@ -157,27 +191,43 @@ pub struct Effect {
/// Random angvel variation
pub angvel_rng: f32,
/// The amount of this effect's parent's velocity to inherit
pub velocity_scale_parent: f32,
/// Parent velocity random variation
pub velocity_scale_parent_rng: f32,
/// The amount of this effect's parent's target velocity to inherit.
/// If there is no target, this is zero.
pub velocity_scale_target: f32,
/// Target velocity random variation
pub velocity_scale_target_rng: f32,
/// Travel direction random variation
pub direction_rng: f32,
/// Fade this effect out over this many seconds as it ends
pub fade: f32,
/// Random fade ariation
/// Random fade variation
pub fade_rng: f32,
/// How to compute this effect's velocity
pub velocity: EffectVelocity,
}
/// How to compute an effect's velocity
#[derive(Debug, Clone)]
pub enum EffectVelocity {
/// Stick to parent
StickyParent,
/// Stick to target.
/// Zero velocity if no target is given.
StickyTarget,
/// Compute velocity from parent and target
Explicit {
/// How much of the parent's velocity to inherit
scale_parent: f32,
/// Random variation of scale_parent
scale_parent_rng: f32,
/// How much of the target's velocity to keep
scale_target: f32,
/// Random variation of scale_target
scale_target_rng: f32,
/// Random variation of travel direction
direction_rng: f32,
},
}
impl crate::Build for Effect {

View File

@ -10,7 +10,7 @@ pub(crate) mod sprite;
pub(crate) mod system;
pub use config::Config;
pub use effect::Effect;
pub use effect::*;
pub use faction::{Faction, Relationship};
pub use outfit::{Gun, Outfit, Projectile, ProjectileCollider};
pub use outfitspace::OutfitSpace;

View File

@ -160,7 +160,7 @@ fn resolve_coordinates(
None => bail!("Could not resolve coordinate label `{l}`"),
};
Ok(resolve_position(&objects, &p, cycle_detector)
.with_context(|| format!("In object {:#?}", l))?)
.with_context(|| format!("in object {:#?}", l))?)
}
}
}
@ -223,7 +223,7 @@ impl crate::Build for System {
objects.push(SystemObject {
sprite: sprite_handle,
pos: resolve_position(&system.object, &obj, cycle_detector)
.with_context(|| format!("In object {:#?}", label))?,
.with_context(|| format!("in object {:#?}", label))?,
size: obj.size,
angle: to_radians(obj.angle.unwrap_or(0.0)),
handle: SystemObjectHandle {

View File

@ -37,3 +37,5 @@ nalgebra = { workspace = true }
clap = { workspace = true }
walkdir = { workspace = true }
image = { workspace = true }
log = { workspace = true }
log4rs = { workspace = true }

View File

@ -1,6 +1,7 @@
use anyhow::{bail, Context, Result};
use galactica_packer::AtlasSet;
use image::io::Reader;
use log::warn;
use std::path::Path;
use walkdir::WalkDir;
@ -22,21 +23,21 @@ pub fn run(asset_root: &Path, cache_target: &Path) -> Result<()> {
match e.path().extension() {
Some(t) => {
if t.to_str() != Some("png") && t.to_str() != Some("jpg") {
println!("[WARNING] {e:#?} is not a png file, skipping.");
warn!("{e:#?} is not a png file, skipping");
continue;
}
}
None => {
println!("[WARNING] {e:#?} is not a png file, skipping.");
warn!("{e:#?} is not a png file, skipping");
continue;
}
}
let path = e.path().to_path_buf();
let reader = Reader::open(&path)
.with_context(|| format!("While reading file `{}`", path.display()))?;
.with_context(|| format!("while reading file `{}`", path.display()))?;
let dim = reader.into_dimensions().with_context(|| {
format!("While reading dimensions of file `{}`", path.display())
format!("while reading dimensions of file `{}`", path.display())
})?;
files.push((path, [dim.0, dim.1]));
total_dim += dim.0 as f64 * dim.1 as f64;
@ -55,7 +56,7 @@ pub fn run(asset_root: &Path, cache_target: &Path) -> Result<()> {
// Our tiling algorithm usually has efficiency better than 80% (~90%, as of writing)
// We need room for error, though, since this check doesn't guarante success.
if total_dim / 0.80 >= (8192.0 * 8192.0 * 16.0) {
bail!("Texture atlas is too small")
bail!("texture atlas is too small")
}
// Create atlas set

View File

@ -64,6 +64,24 @@ impl<'a> Game {
],
);
let a = phys_sim.add_ship(
&ct,
ShipHandle { index: 0 },
FactionHandle { index: 1 },
ShipPersonality::Point,
Point2::new(1000.0, 4000.0),
);
let s = phys_sim.get_ship_mut(&a).unwrap();
s.add_outfits(
&ct,
[
OutfitHandle { index: 0 },
OutfitHandle { index: 1 },
OutfitHandle { index: 2 },
],
);
let a = phys_sim.add_ship(
&ct,
ShipHandle { index: 0 },

View File

@ -10,6 +10,13 @@ use galactica_system::{
phys::{PhysImage, PhysSimShipHandle},
};
use galactica_util::constants::ASSET_CACHE;
use log::LevelFilter;
use log4rs::{
append::console::ConsoleAppender,
config::{Appender, Logger, Root},
encode::pattern::PatternEncoder,
Config,
};
use nalgebra::Vector2;
use std::{
fs,
@ -35,9 +42,34 @@ struct Args {
mod cli;
fn set_up_logger(_args: &Args) -> log4rs::Handle {
// h: highlight with level color
// :<0.40 right align, min 0, max 40
let stdout = ConsoleAppender::builder()
.encoder(Box::new(PatternEncoder::new(
"{d(%H:%M:%S)} | {({h({l})}):5.5} | \x1b[0;34m{M}\x1b[0m — {m:<0.50}{n}",
)))
.build();
let config = Config::builder()
.appender(Appender::builder().build("stdout", Box::new(stdout)))
.logger(Logger::builder().build("wgpu_core", LevelFilter::Warn))
.logger(Logger::builder().build("wgpu_hal", LevelFilter::Warn))
.logger(Logger::builder().build("naga", LevelFilter::Warn))
.logger(Logger::builder().build("cosmic_text", LevelFilter::Warn))
.logger(Logger::builder().build("winit", LevelFilter::Warn))
.logger(Logger::builder().build("mio", LevelFilter::Warn))
.build(Root::builder().appender("stdout").build(LevelFilter::Trace))
.unwrap();
log4rs::init_config(config).unwrap()
}
fn main() -> Result<()> {
let args = Args::parse();
let _ = set_up_logger(&args);
let cache_dir = Path::new(ASSET_CACHE);
fs::create_dir_all(cache_dir)?;

View File

@ -85,9 +85,9 @@ impl AtlasSet {
let mut f = File::open(&path)?;
let mut bytes = Vec::new();
f.read_to_end(&mut bytes)
.with_context(|| format!("While reading file `{}`", path.display()))?;
.with_context(|| format!("while reading file `{}`", path.display()))?;
let img = image::load_from_memory(&bytes)
.with_context(|| format!("While loading file `{}`", path.display()))?;
.with_context(|| format!("while loading file `{}`", path.display()))?;
let image_dim = img.dimensions();
@ -102,7 +102,7 @@ impl AtlasSet {
}
}
if transparent_border {
println!("[WARNING] {} wastes space with a transmparent border",);
warn!("{} wastes space with a transmparent border",);
}
*/

View File

@ -31,3 +31,4 @@ winit = { workspace = true }
wgpu = { workspace = true }
bytemuck = { workspace = true }
glyphon = { workspace = true }
log = { workspace = true }

View File

@ -1,6 +1,7 @@
use crate::globaluniform::GlobalUniform;
// TODO: build script?
// TODO: better preprocessor, build script?
// Not yet, this is ok for now.
/// Basic wgsl preprocesser
pub(crate) fn preprocess_shader(
shader: &str,

View File

@ -5,6 +5,7 @@ use galactica_content::Content;
use galactica_packer::SpriteAtlasImage;
use galactica_util::constants::ASSET_CACHE;
use image::GenericImageView;
use log::info;
use std::{fs::File, io::Read, num::NonZeroU32, path::Path};
use wgpu::BindGroupLayout;
@ -93,7 +94,7 @@ impl TextureArray {
let mut texture_data = Vec::new();
for file in ct.atlas_files() {
println!("opening {file}");
info!("opening {file}");
let p = Path::new(ASSET_CACHE);
let mut f = File::open(p.join(file))?;
let mut bytes = Vec::new();
@ -108,7 +109,7 @@ impl TextureArray {
let mut image_locations = AtlasArray::zeroed();
println!("sending to gpu");
info!("sending to gpu");
for image in ct.get_atlas().iter_images() {
image_locations.data[image.idx.get() as usize] = AtlasImageLocation {
xpos: image.x,

View File

@ -1,7 +1,7 @@
use galactica_content::{Content, EffectHandle, SpriteAutomaton};
use nalgebra::{Rotation2, Vector2};
use galactica_content::{Content, EffectHandle, EffectVelocity, SpriteAutomaton};
use nalgebra::{Point2, Rotation2, Vector2};
use rand::Rng;
use rapier2d::dynamics::{RigidBodyBuilder, RigidBodyHandle, RigidBodyType};
use rapier2d::dynamics::{RevoluteJointBuilder, RigidBodyBuilder, RigidBodyHandle, RigidBodyType};
use crate::phys::{PhysStepResources, PhysWrapper};
@ -35,25 +35,18 @@ impl PhysEffect {
ct: &Content,
wrapper: &mut PhysWrapper,
effect: EffectHandle,
// Where to spawn the particle, in world space.
pos: Vector2<f32>,
parent_angle: f32,
parent_velocity: Vector2<f32>,
target_velocity: Vector2<f32>,
parent: RigidBodyHandle,
target: Option<RigidBodyHandle>,
) -> Self {
let effect = ct.get_effect(effect);
let mut rng = rand::thread_rng();
let velocity = {
let a =
rng.gen_range(-effect.velocity_scale_parent_rng..=effect.velocity_scale_parent_rng);
let b =
rng.gen_range(-effect.velocity_scale_target_rng..=effect.velocity_scale_target_rng);
let velocity = ((effect.velocity_scale_parent + a) * parent_velocity)
+ ((effect.velocity_scale_target + b) * target_velocity);
Rotation2::new(rng.gen_range(-effect.direction_rng..=effect.direction_rng)) * velocity
};
let parent_body = wrapper.get_rigid_body(parent).unwrap();
let parent_angle = parent_body.rotation().angle();
let parent_pos = *parent_body.translation();
let parent_velocity = parent_body.velocity_at_point(parent_body.center_of_mass());
let angvel = if effect.angvel_rng == 0.0 {
effect.angvel
@ -66,22 +59,112 @@ impl PhysEffect {
parent_angle + effect.angle + rng.gen_range(-effect.angle_rng..=effect.angle_rng)
};
let rb = RigidBodyBuilder::new(RigidBodyType::KinematicVelocityBased)
.position(pos.into())
.rotation(angle)
.angvel(angvel)
.linvel(velocity);
let target_velocity = {
if let Some(target) = target {
let target_body = wrapper.get_rigid_body(target).unwrap();
target_body.velocity_at_point(&Point2::new(pos.x, pos.y))
} else {
Vector2::new(0.0, 0.0)
}
};
PhysEffect {
anim: SpriteAutomaton::new(ct, effect.sprite),
rigid_body: wrapper.insert_rigid_body(rb.build()),
lifetime: 0f32
.max(effect.lifetime + rng.gen_range(-effect.lifetime_rng..=effect.lifetime_rng)),
match effect.velocity {
EffectVelocity::StickyTarget | EffectVelocity::StickyParent => {
let rigid_body = wrapper.insert_rigid_body(
RigidBodyBuilder::new(RigidBodyType::Dynamic)
.additional_mass(f32::MIN_POSITIVE)
.position(pos.into())
.rotation(angle)
.angvel(angvel)
.build(),
);
// Make sure size isn't negative. This check should be on EVERY rng!
size: 0f32.max(effect.size + rng.gen_range(-effect.size_rng..=effect.size_rng)),
fade: 0f32.max(effect.fade + rng.gen_range(-effect.fade_rng..=effect.fade_rng)),
is_destroyed: false,
match effect.velocity {
EffectVelocity::StickyParent => {
let d = Rotation2::new(-parent_angle) * (pos - parent_pos);
wrapper.add_joint(
rigid_body,
parent,
RevoluteJointBuilder::new()
.local_anchor1(Point2::new(0.0, 0.0))
.local_anchor2(Point2::new(d.x, d.y)),
)
}
EffectVelocity::StickyTarget => {
if target.is_some() {
let target_body = wrapper.get_rigid_body(target.unwrap()).unwrap();
// Correct for rotation, since joint coordinates are relative
// and input coordinates are in world space.
let d = Rotation2::new(-target_body.rotation().angle())
* (pos - target_body.translation());
wrapper.add_joint(
rigid_body,
target.unwrap(),
RevoluteJointBuilder::new()
.local_anchor1(Point2::new(0.0, 0.0))
.local_anchor2(Point2::new(d.x, d.y)),
)
}
}
_ => unreachable!("lol what?"),
};
PhysEffect {
anim: SpriteAutomaton::new(ct, effect.sprite),
rigid_body,
lifetime: 0f32.max(
effect.lifetime + rng.gen_range(-effect.lifetime_rng..=effect.lifetime_rng),
),
// Make sure size isn't negative. This check should be on EVERY rng!
size: 0f32.max(effect.size + rng.gen_range(-effect.size_rng..=effect.size_rng)),
fade: 0f32.max(effect.fade + rng.gen_range(-effect.fade_rng..=effect.fade_rng)),
is_destroyed: false,
}
}
EffectVelocity::Explicit {
scale_parent,
scale_parent_rng,
scale_target,
scale_target_rng,
direction_rng,
} => {
let velocity = {
let a = rng.gen_range(-scale_parent_rng..=scale_parent_rng);
let b = rng.gen_range(-scale_target_rng..=scale_target_rng);
let velocity = ((scale_parent + a) * parent_velocity)
+ ((scale_target + b) * target_velocity);
Rotation2::new(rng.gen_range(-direction_rng..=direction_rng)) * velocity
};
let rigid_body = wrapper.insert_rigid_body(
RigidBodyBuilder::new(RigidBodyType::KinematicVelocityBased)
.position(pos.into())
.rotation(angle)
.angvel(angvel)
.linvel(velocity)
.build(),
);
PhysEffect {
anim: SpriteAutomaton::new(ct, effect.sprite),
rigid_body,
lifetime: 0f32.max(
effect.lifetime + rng.gen_range(-effect.lifetime_rng..=effect.lifetime_rng),
),
// Make sure size isn't negative. This check should be on EVERY rng!
size: 0f32.max(effect.size + rng.gen_range(-effect.size_rng..=effect.size_rng)),
fade: 0f32.max(effect.fade + rng.gen_range(-effect.fade_rng..=effect.fade_rng)),
is_destroyed: false,
}
}
}
}

View File

@ -1,5 +1,4 @@
use galactica_content::{AnimationState, Content, FactionHandle, Projectile, SpriteAutomaton};
use nalgebra::Vector2;
use rand::Rng;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
@ -97,35 +96,18 @@ impl PhysProjectile {
return;
}
let rigid_body = wrapper.remove_rigid_body(self.rigid_body).unwrap();
let mut rng = rand::thread_rng();
let rb = wrapper.get_rigid_body(self.rigid_body).unwrap();
if expire {
match &self.content.expire_effect {
None => {}
Some(handle) => {
let x = res.ct.get_effect(*handle);
let pos = *rigid_body.translation();
let vel = rigid_body.velocity_at_point(rigid_body.center_of_mass());
let angle = rigid_body.rotation().angle();
let velocity = {
let a = rng
.gen_range(-x.velocity_scale_parent_rng..=x.velocity_scale_parent_rng);
let velocity = (x.velocity_scale_parent + a) * vel;
velocity
};
new.effects.push(PhysEffect::new(
res.ct,
wrapper,
*handle,
pos,
angle,
velocity,
Vector2::new(0.0, 0.0),
*rb.translation(),
self.rigid_body,
None,
));
}
}

View File

@ -88,17 +88,13 @@ impl ShipCollapseSequence {
};
let pos = ship_pos + (ship_rot * pos);
let velocity =
rigid_body.velocity_at_point(&Point2::new(pos.x, pos.y));
new.effects.push(PhysEffect::new(
res.ct,
wrapper,
spawner.effect,
pos,
0.0,
velocity,
Vector2::new(0.0, 0.0),
rigid_body_handle,
None,
));
}
}
@ -133,16 +129,13 @@ impl ShipCollapseSequence {
// Position, adjusted for ship rotation
let pos = ship_pos + (ship_rot * pos);
let vel = rigid_body.velocity_at_point(&Point2::new(pos.x, pos.y));
new.effects.push(PhysEffect::new(
res.ct,
wrapper,
spawner.effect,
pos,
0.0,
vel,
Vector2::new(0.0, 0.0),
rigid_body_handle,
None,
));
}
}

View File

@ -453,16 +453,14 @@ impl PhysShip {
};
let pos = ship_pos + (Rotation2::new(ship_ang) * pos);
let velocity = rigid_body.velocity_at_point(&Point2::new(pos.x, pos.y));
new.effects.push(PhysEffect::new(
res.ct,
wrapper,
e.effect,
pos.into(),
0.0,
velocity,
Vector2::new(0.0, 0.0),
pos,
self.rigid_body,
None,
));
}
}

View File

@ -126,25 +126,17 @@ impl PhysSim {
// Borrow again, we can only have one at a time
let pr = self.wrapper.get_rigid_body(projectile.rigid_body).unwrap();
let pos = *pr.translation();
let angle = pr.rotation().angle();
match &projectile.content.impact_effect {
None => {}
Some(x) => {
let r = ship.rigid_body;
let sr = self.wrapper.get_rigid_body(r).unwrap();
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));
self.effects.push(PhysEffect::new(
res.ct,
&mut self.wrapper,
*x,
pos,
angle,
parent_velocity,
target_velocity,
projectile.rigid_body,
Some(ship.rigid_body),
));
}
};

View File

@ -1,8 +1,8 @@
use crossbeam::channel::Receiver;
use rapier2d::{
dynamics::{
CCDSolver, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet,
RigidBody, RigidBodyHandle, RigidBodySet,
CCDSolver, GenericJoint, ImpulseJointSet, IntegrationParameters, IslandManager,
MultibodyJointSet, RigidBody, RigidBodyHandle, RigidBodySet,
},
geometry::{BroadPhase, Collider, ColliderHandle, ColliderSet, CollisionEvent, NarrowPhase},
na::vector,
@ -16,12 +16,12 @@ pub struct PhysWrapper {
im: IslandManager,
bp: BroadPhase,
np: NarrowPhase,
ij: ImpulseJointSet,
mj: MultibodyJointSet,
ccd: CCDSolver,
rigid_body_set: RigidBodySet,
collider_set: ColliderSet,
joint_set: ImpulseJointSet,
collision_handler: ChannelEventCollector,
@ -42,7 +42,6 @@ impl PhysWrapper {
im: IslandManager::new(),
bp: BroadPhase::new(),
np: NarrowPhase::new(),
ij: ImpulseJointSet::new(),
mj: MultibodyJointSet::new(),
ccd: CCDSolver::new(),
collision_queue,
@ -50,6 +49,7 @@ impl PhysWrapper {
rigid_body_set: RigidBodySet::new(),
collider_set: ColliderSet::new(),
joint_set: ImpulseJointSet::new(),
}
}
@ -64,7 +64,7 @@ impl PhysWrapper {
&mut self.np,
&mut self.rigid_body_set,
&mut self.collider_set,
&mut self.ij,
&mut self.joint_set,
&mut self.mj,
&mut self.ccd,
None,
@ -79,7 +79,7 @@ impl PhysWrapper {
body,
&mut self.im,
&mut self.collider_set,
&mut self.ij,
&mut self.joint_set,
&mut self.mj,
true,
);
@ -121,4 +121,14 @@ impl PhysWrapper {
self.collider_set
.insert_with_parent(collider, parent_handle, &mut self.rigid_body_set)
}
/// Add an impulse joint between two bodies
pub fn add_joint(
&mut self,
body1: RigidBodyHandle,
body2: RigidBodyHandle,
joint: impl Into<GenericJoint>,
) {
self.joint_set.insert(body1, body2, joint, false);
}
}

View File

@ -122,7 +122,7 @@ impl Timing {
let f = self.physics_sim.as_secs_f32();
s.push_str(&format!("Phys {:6.00} fps\n", 1.0 / f));
s.push_str(&format!(
"Step {:6.02}%\n",
"Step {:5.02}%\n",
100.0 * self.physics_step.as_secs_f32() / f
));
s.push_str(&format!(