Added fonts & config
parent
188dc9eb89
commit
7e12a0e26d
|
@ -66,6 +66,5 @@ 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"
|
||||||
|
|
||||||
# Glyphon's crates.io release doesn't support wgpu 0.18 yet
|
# Glyphon's crates.io release doesn't support wgpu 0.18 yet
|
||||||
glyphon = { git = "https://github.com/grovesNL/glyphon.git", branch = "main" }
|
glyphon = { git = "https://github.com/grovesNL/glyphon.git", branch = "main" }
|
||||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 1674e86c1edcbd119d94516950d2d274b46a19d4
|
Subproject commit 9746acb16c6e2c5f6f232e8d1b53e27fb9ef486e
|
|
@ -0,0 +1,38 @@
|
||||||
|
[config]
|
||||||
|
# Per-game config values.
|
||||||
|
# These should rarely be changed.
|
||||||
|
|
||||||
|
sprite_root = "render"
|
||||||
|
|
||||||
|
fonts.files = [
|
||||||
|
"fonts/PTAstraSans-Bold.ttf",
|
||||||
|
"fonts/PTAstraSans-BoldItalic.ttf",
|
||||||
|
"fonts/PTAstraSans-Italic.ttf",
|
||||||
|
"fonts/PTAstraSans-Regular.ttf",
|
||||||
|
"fonts/PTAstraSerif-Bold.ttf",
|
||||||
|
"fonts/PTAstraSerif-BoldItalic.ttf",
|
||||||
|
"fonts/PTAstraSerif-Italic.ttf",
|
||||||
|
"fonts/PTAstraSerif-Regular.ttf",
|
||||||
|
"fonts/PTMono-Regular.ttf",
|
||||||
|
]
|
||||||
|
|
||||||
|
fonts.serif = "PT Astra Serif"
|
||||||
|
fonts.sans = "PT Astra Sans"
|
||||||
|
fonts.mono = "PT Mono"
|
||||||
|
|
||||||
|
# Size range for starfield stars, in game units.
|
||||||
|
# This is scaled for zoom, but NOT for distance.
|
||||||
|
starfield.min_size = 0.2
|
||||||
|
starfield.max_size = 1.8
|
||||||
|
|
||||||
|
# Z-axis (parallax) range for starfield stars
|
||||||
|
starfield.min_dist = 75.0
|
||||||
|
starfield.max_dist = 200.0
|
||||||
|
# Name of starfield sprite
|
||||||
|
starfield.sprite = "starfield"
|
||||||
|
|
||||||
|
|
||||||
|
# Zoom level bounds.
|
||||||
|
# Zoom is measured as "window height in game units."
|
||||||
|
zoom_min = 200.0
|
||||||
|
zoom_max = 2000.0
|
|
@ -7,7 +7,7 @@ mod handle;
|
||||||
mod part;
|
mod part;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use galactica_packer::{SpriteAtlas, SpriteAtlasImage};
|
use galactica_packer::{SpriteAtlas, SpriteAtlasImage};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -28,7 +28,10 @@ mod syntax {
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{collections::HashMap, fmt::Display, hash::Hash};
|
use std::{collections::HashMap, fmt::Display, hash::Hash};
|
||||||
|
|
||||||
use crate::part::{effect, faction, outfit, ship, sprite, system};
|
use crate::{
|
||||||
|
config,
|
||||||
|
part::{effect, faction, outfit, ship, sprite, system},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
|
@ -38,6 +41,7 @@ mod syntax {
|
||||||
pub sprite: Option<HashMap<String, sprite::syntax::Sprite>>,
|
pub sprite: Option<HashMap<String, sprite::syntax::Sprite>>,
|
||||||
pub faction: Option<HashMap<String, faction::syntax::Faction>>,
|
pub faction: Option<HashMap<String, faction::syntax::Faction>>,
|
||||||
pub effect: Option<HashMap<String, effect::syntax::Effect>>,
|
pub effect: Option<HashMap<String, effect::syntax::Effect>>,
|
||||||
|
pub config: Option<config::syntax::Config>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_hashmap<K, V>(
|
fn merge_hashmap<K, V>(
|
||||||
|
@ -74,6 +78,7 @@ mod syntax {
|
||||||
sprite: None,
|
sprite: None,
|
||||||
faction: None,
|
faction: None,
|
||||||
effect: None,
|
effect: None,
|
||||||
|
config: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +94,13 @@ mod syntax {
|
||||||
.with_context(|| "while merging factions")?;
|
.with_context(|| "while merging factions")?;
|
||||||
merge_hashmap(&mut self.effect, other.effect)
|
merge_hashmap(&mut self.effect, other.effect)
|
||||||
.with_context(|| "while merging effects")?;
|
.with_context(|| "while merging effects")?;
|
||||||
|
if self.config.is_some() {
|
||||||
|
if other.config.is_some() {
|
||||||
|
bail!("invalid content dir, multiple config tables")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.config = other.config;
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,12 +136,6 @@ impl ContentBuildContext {
|
||||||
/// Represents static game content
|
/// Represents static game content
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Content {
|
pub struct Content {
|
||||||
/* Configuration values */
|
|
||||||
/// Root directory for image
|
|
||||||
image_root: PathBuf,
|
|
||||||
/// Name of starfield sprite
|
|
||||||
starfield_sprite_name: String,
|
|
||||||
|
|
||||||
/// Sprites
|
/// Sprites
|
||||||
pub sprites: Vec<Sprite>,
|
pub sprites: Vec<Sprite>,
|
||||||
/// Map strings to texture names.
|
/// Map strings to texture names.
|
||||||
|
@ -147,6 +153,7 @@ pub struct Content {
|
||||||
systems: Vec<System>,
|
systems: Vec<System>,
|
||||||
factions: Vec<Faction>,
|
factions: Vec<Faction>,
|
||||||
effects: Vec<Effect>,
|
effects: Vec<Effect>,
|
||||||
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading methods
|
// Loading methods
|
||||||
|
@ -159,12 +166,7 @@ impl Content {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load content from a directory.
|
/// Load content from a directory.
|
||||||
pub fn load_dir(
|
pub fn load_dir(path: PathBuf, asset_root: PathBuf, atlas_index: PathBuf) -> Result<Self> {
|
||||||
path: PathBuf,
|
|
||||||
texture_root: PathBuf,
|
|
||||||
atlas_index: PathBuf,
|
|
||||||
starfield_texture_name: String,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let mut root = syntax::Root::new();
|
let mut root = syntax::Root::new();
|
||||||
|
|
||||||
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
||||||
|
@ -202,6 +204,15 @@ impl Content {
|
||||||
let mut build_context = ContentBuildContext::new();
|
let mut build_context = ContentBuildContext::new();
|
||||||
|
|
||||||
let mut content = Self {
|
let mut content = Self {
|
||||||
|
config: {
|
||||||
|
if let Some(c) = root.config {
|
||||||
|
c.build(&asset_root)
|
||||||
|
.with_context(|| "while parsing config table")?
|
||||||
|
} else {
|
||||||
|
bail!("failed loading content: no config table specified")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
sprite_atlas: atlas,
|
sprite_atlas: atlas,
|
||||||
systems: Vec::new(),
|
systems: Vec::new(),
|
||||||
ships: Vec::new(),
|
ships: Vec::new(),
|
||||||
|
@ -212,8 +223,6 @@ impl Content {
|
||||||
effects: Vec::new(),
|
effects: Vec::new(),
|
||||||
sprite_index: HashMap::new(),
|
sprite_index: HashMap::new(),
|
||||||
starfield_handle: None,
|
starfield_handle: None,
|
||||||
image_root: texture_root,
|
|
||||||
starfield_sprite_name: starfield_texture_name,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: enforce sprite and image limits
|
// TODO: enforce sprite and image limits
|
||||||
|
@ -333,4 +342,9 @@ impl Content {
|
||||||
pub fn get_effect(&self, h: EffectHandle) -> &Effect {
|
pub fn get_effect(&self, h: EffectHandle) -> &Effect {
|
||||||
return &self.effects[h.index];
|
return &self.effects[h.index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get content configuration
|
||||||
|
pub fn get_config(&self) -> &Config {
|
||||||
|
return &self.config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub(crate) mod syntax {
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
// Raw serde syntax structs.
|
||||||
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub fonts: Fonts,
|
||||||
|
pub sprite_root: PathBuf,
|
||||||
|
pub starfield: Starfield,
|
||||||
|
pub zoom_min: f32,
|
||||||
|
pub zoom_max: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
// TODO: clean up build trait
|
||||||
|
pub fn build(self, asset_root: &Path) -> Result<super::Config> {
|
||||||
|
for i in &self.fonts.files {
|
||||||
|
if !asset_root.join(i).exists() {
|
||||||
|
bail!("font file `{}` doesn't exist", i.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let starfield_density = 0.01;
|
||||||
|
let starfield_size = self.starfield.min_dist * self.zoom_max;
|
||||||
|
let starfield_count = (starfield_size * starfield_density) as i32;
|
||||||
|
|
||||||
|
// 12, because that should be enough to tile any screen.
|
||||||
|
// Starfield squares are tiled to cover the viewport, adapting to any screen ratio.
|
||||||
|
// An insufficient limit will result in some tiles not being drawn
|
||||||
|
let starfield_instance_limit = 12 * starfield_count as u64;
|
||||||
|
|
||||||
|
return Ok(super::Config {
|
||||||
|
sprite_root: asset_root.join(self.sprite_root),
|
||||||
|
font_files: self
|
||||||
|
.fonts
|
||||||
|
.files
|
||||||
|
.iter()
|
||||||
|
.map(|i| asset_root.join(i))
|
||||||
|
.collect(),
|
||||||
|
|
||||||
|
font_sans: self.fonts.sans,
|
||||||
|
font_serif: self.fonts.serif,
|
||||||
|
font_mono: self.fonts.mono,
|
||||||
|
//font_cursive: self.fonts.cursive,
|
||||||
|
//font_fantasy: self.fonts.fantasy,
|
||||||
|
starfield_max_dist: self.starfield.max_dist,
|
||||||
|
starfield_min_dist: self.starfield.min_dist,
|
||||||
|
starfield_max_size: self.starfield.max_size,
|
||||||
|
starfield_min_size: self.starfield.min_size,
|
||||||
|
starfield_sprite: self.starfield.sprite,
|
||||||
|
starfield_count,
|
||||||
|
starfield_density,
|
||||||
|
starfield_size,
|
||||||
|
starfield_instance_limit,
|
||||||
|
zoom_max: self.zoom_max,
|
||||||
|
zoom_min: self.zoom_min,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Fonts {
|
||||||
|
pub files: Vec<PathBuf>,
|
||||||
|
pub sans: String,
|
||||||
|
pub serif: String,
|
||||||
|
pub mono: String,
|
||||||
|
//pub cursive: String,
|
||||||
|
//pub fantasy: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Starfield {
|
||||||
|
pub min_size: f32,
|
||||||
|
pub max_size: f32,
|
||||||
|
pub min_dist: f32,
|
||||||
|
pub max_dist: f32,
|
||||||
|
pub sprite: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Content configuration
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
/// The directory where all images are stored.
|
||||||
|
/// Image paths are always interpreted relative to this path.
|
||||||
|
/// This is a subdirectory of the asset root.
|
||||||
|
pub sprite_root: PathBuf,
|
||||||
|
|
||||||
|
/// List of font files to load
|
||||||
|
pub font_files: Vec<PathBuf>,
|
||||||
|
|
||||||
|
/// Sans Serif font family name
|
||||||
|
pub font_sans: String,
|
||||||
|
|
||||||
|
/// Serif font family name
|
||||||
|
pub font_serif: String,
|
||||||
|
|
||||||
|
/// Monospace font family name
|
||||||
|
pub font_mono: String,
|
||||||
|
//pub font_cursive: String,
|
||||||
|
//pub font_fantasy: String,
|
||||||
|
/// Min size of starfield sprite, in game units
|
||||||
|
pub starfield_min_size: f32,
|
||||||
|
|
||||||
|
/// Max size of starfield sprite, in game units
|
||||||
|
pub starfield_max_size: f32,
|
||||||
|
|
||||||
|
/// Minimum z-distance of starfield star, in game units
|
||||||
|
pub starfield_min_dist: f32,
|
||||||
|
|
||||||
|
/// Maximum z-distance of starfield star, in game units
|
||||||
|
pub starfield_max_dist: f32,
|
||||||
|
|
||||||
|
/// Name of starfield sprite
|
||||||
|
pub starfield_sprite: String,
|
||||||
|
|
||||||
|
/// Size of a square starfield tile, in game units.
|
||||||
|
/// A tile of size STARFIELD_Z_MAX * screen-size-in-game-units
|
||||||
|
/// will completely cover a (square) screen.
|
||||||
|
/// This should be big enough to cover the height of the screen at max zoom.
|
||||||
|
pub starfield_size: f32,
|
||||||
|
|
||||||
|
/// Average number of stars per game unit
|
||||||
|
pub starfield_density: f32,
|
||||||
|
|
||||||
|
/// Number of stars in one starfield tile
|
||||||
|
/// Must be positive
|
||||||
|
pub starfield_count: i32,
|
||||||
|
|
||||||
|
// TODO: this shouldn't be here, it depends on graphics implementation
|
||||||
|
/// The maximum number of starfield sprites we can create
|
||||||
|
pub starfield_instance_limit: u64,
|
||||||
|
|
||||||
|
/// Minimum zoom, in game units
|
||||||
|
pub zoom_min: f32,
|
||||||
|
|
||||||
|
/// Maximum zoom,in game units
|
||||||
|
pub zoom_max: f32,
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
//! Content parts
|
//! Content parts
|
||||||
|
|
||||||
|
pub(crate) mod config;
|
||||||
pub(crate) mod effect;
|
pub(crate) mod effect;
|
||||||
pub(crate) mod faction;
|
pub(crate) mod faction;
|
||||||
pub(crate) mod outfit;
|
pub(crate) mod outfit;
|
||||||
|
@ -8,6 +9,7 @@ pub(crate) mod ship;
|
||||||
pub(crate) mod sprite;
|
pub(crate) mod sprite;
|
||||||
pub(crate) mod system;
|
pub(crate) mod system;
|
||||||
|
|
||||||
|
pub use config::Config;
|
||||||
pub use effect::Effect;
|
pub use effect::Effect;
|
||||||
pub use faction::{Faction, Relationship};
|
pub use faction::{Faction, Relationship};
|
||||||
pub use outfit::{Gun, Outfit, Projectile, ProjectileCollider};
|
pub use outfit::{Gun, Outfit, Projectile, ProjectileCollider};
|
||||||
|
|
|
@ -120,7 +120,7 @@ impl crate::Build for Sprite {
|
||||||
for (sprite_name, t) in sprites {
|
for (sprite_name, t) in sprites {
|
||||||
match t {
|
match t {
|
||||||
syntax::Sprite::Static(t) => {
|
syntax::Sprite::Static(t) => {
|
||||||
let file = content.image_root.join(&t.file);
|
let file = content.config.sprite_root.join(&t.file);
|
||||||
let reader = Reader::open(&file).with_context(|| {
|
let reader = Reader::open(&file).with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"Failed to read file `{}` in sprite `{}`",
|
"Failed to read file `{}` in sprite `{}`",
|
||||||
|
@ -141,7 +141,7 @@ impl crate::Build for Sprite {
|
||||||
aspect: dim.0 as f32 / dim.1 as f32,
|
aspect: dim.0 as f32 / dim.1 as f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
if sprite_name == content.starfield_sprite_name {
|
if sprite_name == content.config.starfield_sprite {
|
||||||
if content.starfield_handle.is_none() {
|
if content.starfield_handle.is_none() {
|
||||||
content.starfield_handle = Some(h)
|
content.starfield_handle = Some(h)
|
||||||
} else {
|
} else {
|
||||||
|
@ -166,7 +166,7 @@ impl crate::Build for Sprite {
|
||||||
syntax::Sprite::Frames(t) => {
|
syntax::Sprite::Frames(t) => {
|
||||||
let mut dim = None;
|
let mut dim = None;
|
||||||
for f in &t.frames {
|
for f in &t.frames {
|
||||||
let file = content.image_root.join(f);
|
let file = content.config.sprite_root.join(f);
|
||||||
let reader = Reader::open(&file).with_context(|| {
|
let reader = Reader::open(&file).with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"Failed to read file `{}` in sprite `{}`",
|
"Failed to read file `{}` in sprite `{}`",
|
||||||
|
@ -200,7 +200,7 @@ impl crate::Build for Sprite {
|
||||||
aspect: dim.0 as f32 / dim.1 as f32,
|
aspect: dim.0 as f32 / dim.1 as f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
if sprite_name == content.starfield_sprite_name {
|
if sprite_name == content.config.starfield_sprite {
|
||||||
unreachable!("Starfield texture may not be animated")
|
unreachable!("Starfield texture may not be animated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ impl crate::Build for Sprite {
|
||||||
if content.starfield_handle.is_none() {
|
if content.starfield_handle.is_none() {
|
||||||
bail!(
|
bail!(
|
||||||
"Could not find a starfield texture (name: `{}`)",
|
"Could not find a starfield texture (name: `{}`)",
|
||||||
content.starfield_sprite_name
|
content.config.starfield_sprite
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
|
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
|
||||||
use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle};
|
use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle};
|
||||||
use galactica_util::{
|
use galactica_util::timing::Timing;
|
||||||
constants::{ZOOM_MAX, ZOOM_MIN},
|
|
||||||
timing::Timing,
|
|
||||||
};
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
|
@ -21,8 +18,9 @@ pub struct Game {
|
||||||
start_instant: Instant,
|
start_instant: Instant,
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
|
|
||||||
galaxy: Galaxy,
|
ct: Content,
|
||||||
content: Content,
|
gx: Galaxy,
|
||||||
|
|
||||||
systemsim: SystemSim,
|
systemsim: SystemSim,
|
||||||
|
|
||||||
new_particles: Vec<ParticleBuilder>,
|
new_particles: Vec<ParticleBuilder>,
|
||||||
|
@ -86,8 +84,8 @@ impl Game {
|
||||||
paused: false,
|
paused: false,
|
||||||
time_scale: 1.0,
|
time_scale: 1.0,
|
||||||
systemsim: physics,
|
systemsim: physics,
|
||||||
galaxy,
|
gx: galaxy,
|
||||||
content: ct,
|
ct,
|
||||||
new_particles: Vec::new(),
|
new_particles: Vec::new(),
|
||||||
timing: Timing::new(),
|
timing: Timing::new(),
|
||||||
}
|
}
|
||||||
|
@ -123,7 +121,7 @@ impl Game {
|
||||||
|
|
||||||
self.timing.start_frame();
|
self.timing.start_frame();
|
||||||
self.timing.start_galaxy();
|
self.timing.start_galaxy();
|
||||||
self.galaxy.step(t);
|
self.gx.step(t);
|
||||||
self.timing.mark_galaxy();
|
self.timing.mark_galaxy();
|
||||||
|
|
||||||
self.systemsim.step(StepResources {
|
self.systemsim.step(StepResources {
|
||||||
|
@ -134,15 +132,16 @@ impl Game {
|
||||||
thrust: self.input.key_thrust,
|
thrust: self.input.key_thrust,
|
||||||
guns: self.input.key_guns,
|
guns: self.input.key_guns,
|
||||||
},
|
},
|
||||||
ct: &self.content,
|
ct: &self.ct,
|
||||||
gx: &mut self.galaxy,
|
gx: &mut self.gx,
|
||||||
particles: &mut self.new_particles,
|
particles: &mut self.new_particles,
|
||||||
timing: &mut self.timing,
|
timing: &mut self.timing,
|
||||||
t,
|
t,
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.input.v_scroll != 0.0 {
|
if self.input.v_scroll != 0.0 {
|
||||||
self.camera.zoom = (self.camera.zoom + self.input.v_scroll).clamp(ZOOM_MIN, ZOOM_MAX);
|
self.camera.zoom = (self.camera.zoom + self.input.v_scroll)
|
||||||
|
.clamp(self.ct.get_config().zoom_min, self.ct.get_config().zoom_max);
|
||||||
self.input.v_scroll = 0.0;
|
self.input.v_scroll = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,13 +160,17 @@ impl Game {
|
||||||
camera_pos: self.camera.pos,
|
camera_pos: self.camera.pos,
|
||||||
camera_zoom: self.camera.zoom,
|
camera_zoom: self.camera.zoom,
|
||||||
current_time: self.start_instant.elapsed().as_secs_f32(),
|
current_time: self.start_instant.elapsed().as_secs_f32(),
|
||||||
content: &self.content,
|
ct: &self.ct,
|
||||||
systemsim: &self.systemsim, // TODO: maybe system should be stored here?
|
systemsim: &self.systemsim, // TODO: maybe system should be stored here?
|
||||||
particles: &mut self.new_particles,
|
particles: &mut self.new_particles,
|
||||||
player_data: self.player,
|
player_data: self.player,
|
||||||
data: &self.galaxy,
|
gx: &self.gx,
|
||||||
current_system: SystemHandle { index: 0 },
|
current_system: SystemHandle { index: 0 },
|
||||||
timing: &mut self.timing,
|
timing: &mut self.timing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_content(&self) -> &Content {
|
||||||
|
&self.ct
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ mod inputstatus;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use galactica_util::constants::{ASSET_CACHE, CONTENT_ROOT, IMAGE_ROOT, STARFIELD_SPRITE_NAME};
|
use galactica_util::constants::ASSET_CACHE;
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -27,19 +27,17 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
// TODO: pretty error if missing
|
// TODO: pretty error if missing
|
||||||
let content = Content::load_dir(
|
let content = Content::load_dir(
|
||||||
PathBuf::from(CONTENT_ROOT),
|
PathBuf::from("./content"),
|
||||||
PathBuf::from(IMAGE_ROOT),
|
PathBuf::from("./assets"),
|
||||||
atlas_index,
|
atlas_index,
|
||||||
STARFIELD_SPRITE_NAME.to_owned(),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?;
|
let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?;
|
||||||
gpu.init();
|
gpu.init(&content);
|
||||||
|
|
||||||
let mut game = game::Game::new(content);
|
let mut game = game::Game::new(content);
|
||||||
gpu.update_starfield_buffer();
|
|
||||||
game.set_camera_aspect(
|
game.set_camera_aspect(
|
||||||
gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32,
|
gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32,
|
||||||
);
|
);
|
||||||
|
@ -49,7 +47,7 @@ fn main() -> Result<()> {
|
||||||
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
|
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
|
||||||
match gpu.render(game.get_frame_state()) {
|
match gpu.render(game.get_frame_state()) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(wgpu::SurfaceError::Lost) => gpu.resize(),
|
Err(wgpu::SurfaceError::Lost) => gpu.resize(game.get_content()),
|
||||||
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
|
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
|
||||||
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
||||||
Err(e) => eprintln!("{:?}", e),
|
Err(e) => eprintln!("{:?}", e),
|
||||||
|
@ -85,14 +83,14 @@ fn main() -> Result<()> {
|
||||||
game.process_scroll(delta, phase);
|
game.process_scroll(delta, phase);
|
||||||
}
|
}
|
||||||
WindowEvent::Resized(_) => {
|
WindowEvent::Resized(_) => {
|
||||||
gpu.resize();
|
gpu.resize(game.get_content());
|
||||||
game.set_camera_aspect(
|
game.set_camera_aspect(
|
||||||
gpu.window().inner_size().width as f32
|
gpu.window().inner_size().width as f32
|
||||||
/ gpu.window().inner_size().height as f32,
|
/ gpu.window().inner_size().height as f32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
WindowEvent::ScaleFactorChanged { .. } => {
|
WindowEvent::ScaleFactorChanged { .. } => {
|
||||||
gpu.resize();
|
gpu.resize(game.get_content());
|
||||||
game.set_camera_aspect(
|
game.set_camera_aspect(
|
||||||
gpu.window().inner_size().width as f32
|
gpu.window().inner_size().width as f32
|
||||||
/ gpu.window().inner_size().height as f32,
|
/ gpu.window().inner_size().height as f32,
|
||||||
|
|
|
@ -32,10 +32,10 @@ pub struct RenderInput<'a> {
|
||||||
pub current_time: f32,
|
pub current_time: f32,
|
||||||
|
|
||||||
/// Game content
|
/// Game content
|
||||||
pub content: &'a Content,
|
pub ct: &'a Content,
|
||||||
|
|
||||||
/// Game data
|
/// Game data
|
||||||
pub data: &'a Galaxy,
|
pub gx: &'a Galaxy,
|
||||||
|
|
||||||
/// Particles to spawn during this frame
|
/// Particles to spawn during this frame
|
||||||
pub particles: &'a mut Vec<ParticleBuilder>,
|
pub particles: &'a mut Vec<ParticleBuilder>,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
|
use galactica_content::Content;
|
||||||
use wgpu;
|
use wgpu;
|
||||||
use winit;
|
use winit;
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ impl GPUState {
|
||||||
|
|
||||||
/// Update window size.
|
/// Update window size.
|
||||||
/// This should be called whenever our window is resized.
|
/// This should be called whenever our window is resized.
|
||||||
pub fn resize(&mut self) {
|
pub fn resize(&mut self, ct: &Content) {
|
||||||
let new_size = self.state.window.inner_size();
|
let new_size = self.state.window.inner_size();
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
self.state.window_size = new_size;
|
self.state.window_size = new_size;
|
||||||
|
@ -49,23 +50,11 @@ impl GPUState {
|
||||||
self.config.height = new_size.height;
|
self.config.height = new_size.height;
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(&self.device, &self.config);
|
||||||
}
|
}
|
||||||
self.update_starfield_buffer()
|
self.starfield.update_buffer(ct, &mut self.state);
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a StarfieldInstance for each star that needs to be drawn.
|
|
||||||
/// Will panic if STARFIELD_INSTANCE_LIMIT is exceeded.
|
|
||||||
///
|
|
||||||
/// Starfield data rarely changes, so this is called only when it's needed.
|
|
||||||
pub fn update_starfield_buffer(&mut self) {
|
|
||||||
self.state.queue.write_buffer(
|
|
||||||
&self.state.vertex_buffers.starfield.instances,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&self.starfield.make_instances(self.state.window_aspect)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the rendering engine
|
/// Initialize the rendering engine
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self, ct: &Content) {
|
||||||
// Update global values
|
// Update global values
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.atlas_buffer,
|
&self.state.global_uniform.atlas_buffer,
|
||||||
|
@ -78,6 +67,6 @@ impl GPUState {
|
||||||
bytemuck::cast_slice(&[self.texture_array.sprite_data]),
|
bytemuck::cast_slice(&[self.texture_array.sprite_data]),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.update_starfield_buffer();
|
self.starfield.update_buffer(ct, &mut self.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use galactica_util::constants::{
|
use galactica_util::constants::{
|
||||||
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, STARFIELD_SPRITE_INSTANCE_LIMIT,
|
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT,
|
||||||
UI_SPRITE_INSTANCE_LIMIT,
|
|
||||||
};
|
};
|
||||||
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -110,7 +109,7 @@ impl GPUState {
|
||||||
&device,
|
&device,
|
||||||
Some(SPRITE_VERTICES),
|
Some(SPRITE_VERTICES),
|
||||||
Some(SPRITE_INDICES),
|
Some(SPRITE_INDICES),
|
||||||
STARFIELD_SPRITE_INSTANCE_LIMIT,
|
ct.get_config().starfield_instance_limit,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
||||||
|
@ -150,7 +149,32 @@ impl GPUState {
|
||||||
|
|
||||||
// Text renderer
|
// Text renderer
|
||||||
let mut text_atlas = TextAtlas::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb);
|
let mut text_atlas = TextAtlas::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb);
|
||||||
let text_font_system = FontSystem::new();
|
let mut text_font_system = FontSystem::new_with_locale_and_db(
|
||||||
|
"en-US".to_string(),
|
||||||
|
glyphon::fontdb::Database::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let conf = ct.get_config();
|
||||||
|
for font in &conf.font_files {
|
||||||
|
text_font_system.db_mut().load_font_file(font)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: nice error if no family with this name is found
|
||||||
|
text_font_system
|
||||||
|
.db_mut()
|
||||||
|
.set_sans_serif_family(conf.font_sans.clone());
|
||||||
|
text_font_system
|
||||||
|
.db_mut()
|
||||||
|
.set_serif_family(conf.font_serif.clone());
|
||||||
|
text_font_system
|
||||||
|
.db_mut()
|
||||||
|
.set_monospace_family(conf.font_mono.clone());
|
||||||
|
//text_font_system
|
||||||
|
// .db_mut()
|
||||||
|
// .set_cursive_family(conf.font_cursive.clone());
|
||||||
|
//text_font_system
|
||||||
|
// .db_mut()
|
||||||
|
// .set_fantasy_family(conf.font_fantasy.clone());
|
||||||
|
|
||||||
let text_cache = SwashCache::new();
|
let text_cache = SwashCache::new();
|
||||||
let text_renderer = TextRenderer::new(
|
let text_renderer = TextRenderer::new(
|
||||||
|
@ -238,7 +262,7 @@ impl GPUState {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let mut starfield = Starfield::new();
|
let mut starfield = Starfield::new();
|
||||||
starfield.regenerate();
|
starfield.regenerate(ct);
|
||||||
|
|
||||||
let mut state = RenderState {
|
let mut state = RenderState {
|
||||||
queue,
|
queue,
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use galactica_util::constants::{
|
use galactica_util::constants::PARTICLE_SPRITE_INSTANCE_LIMIT;
|
||||||
PARTICLE_SPRITE_INSTANCE_LIMIT, STARFIELD_SIZE, STARFIELD_SIZE_MAX, STARFIELD_SIZE_MIN,
|
|
||||||
ZOOM_MAX, ZOOM_MIN,
|
|
||||||
};
|
|
||||||
use glyphon::Resolution;
|
use glyphon::Resolution;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
@ -56,7 +53,7 @@ impl super::GPUState {
|
||||||
self.state.vertex_buffers.radialbar_counter = 0;
|
self.state.vertex_buffers.radialbar_counter = 0;
|
||||||
// Don't reset particle counter, it's special
|
// Don't reset particle counter, it's special
|
||||||
|
|
||||||
let s = input.content.get_starfield_handle();
|
let s = input.ct.get_starfield_handle();
|
||||||
|
|
||||||
// Update global values
|
// Update global values
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
|
@ -65,7 +62,10 @@ impl super::GPUState {
|
||||||
bytemuck::cast_slice(&[GlobalDataContent {
|
bytemuck::cast_slice(&[GlobalDataContent {
|
||||||
camera_position: input.camera_pos.into(),
|
camera_position: input.camera_pos.into(),
|
||||||
camera_zoom: [input.camera_zoom, 0.0],
|
camera_zoom: [input.camera_zoom, 0.0],
|
||||||
camera_zoom_limits: [ZOOM_MIN, ZOOM_MAX],
|
camera_zoom_limits: [
|
||||||
|
input.ct.get_config().zoom_min,
|
||||||
|
input.ct.get_config().zoom_max,
|
||||||
|
],
|
||||||
window_size: [
|
window_size: [
|
||||||
self.state.window_size.width as f32,
|
self.state.window_size.width as f32,
|
||||||
self.state.window_size.height as f32,
|
self.state.window_size.height as f32,
|
||||||
|
@ -73,8 +73,11 @@ impl super::GPUState {
|
||||||
window_scale: [self.state.window.scale_factor() as f32, 0.0],
|
window_scale: [self.state.window.scale_factor() as f32, 0.0],
|
||||||
window_aspect: [self.state.window_aspect, 0.0],
|
window_aspect: [self.state.window_aspect, 0.0],
|
||||||
starfield_sprite: [s.get_index(), 0],
|
starfield_sprite: [s.get_index(), 0],
|
||||||
starfield_tile_size: [STARFIELD_SIZE as f32, 0.0],
|
starfield_tile_size: [input.ct.get_config().starfield_size, 0.0],
|
||||||
starfield_size_limits: [STARFIELD_SIZE_MIN, STARFIELD_SIZE_MAX],
|
starfield_size_limits: [
|
||||||
|
input.ct.get_config().starfield_min_size,
|
||||||
|
input.ct.get_config().starfield_max_size,
|
||||||
|
],
|
||||||
current_time: [input.current_time, 0.0],
|
current_time: [input.current_time, 0.0],
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl GPUState {
|
||||||
let ship_pos = util::rigidbody_position(&r);
|
let ship_pos = util::rigidbody_position(&r);
|
||||||
let ship_rot = util::rigidbody_rotation(r);
|
let ship_rot = util::rigidbody_rotation(r);
|
||||||
let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix!
|
let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix!
|
||||||
let ship_cnt = state.content.get_ship(s.data_handle.content_handle());
|
let ship_cnt = state.ct.get_ship(s.data_handle.content_handle());
|
||||||
|
|
||||||
// Position adjusted for parallax
|
// Position adjusted for parallax
|
||||||
// TODO: adjust parallax for zoom?
|
// TODO: adjust parallax for zoom?
|
||||||
|
@ -83,12 +83,12 @@ impl GPUState {
|
||||||
// This will be None if this ship is dead.
|
// This will be None if this ship is dead.
|
||||||
// (physics object stays around to complete the death animation)
|
// (physics object stays around to complete the death animation)
|
||||||
// If that is the case, we're done, no flares to draw anyway!
|
// If that is the case, we're done, no flares to draw anyway!
|
||||||
let ship = match state.data.get_ship(s.data_handle) {
|
let ship = match state.gx.get_ship(s.data_handle) {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
};
|
};
|
||||||
|
|
||||||
let flare = ship.get_outfits().get_flare_sprite(state.content);
|
let flare = ship.get_outfits().get_flare_sprite(state.ct);
|
||||||
if s.get_controls().thrust && flare.is_some() {
|
if s.get_controls().thrust && flare.is_some() {
|
||||||
for engine_point in &ship_cnt.engines {
|
for engine_point in &ship_cnt.engines {
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
|
@ -204,7 +204,7 @@ impl GPUState {
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
let system = state.content.get_system(state.current_system);
|
let system = state.ct.get_system(state.current_system);
|
||||||
|
|
||||||
for o in &system.objects {
|
for o in &system.objects {
|
||||||
// Position adjusted for parallax
|
// Position adjusted for parallax
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use cgmath::{Point2, Point3, Vector2, Vector3};
|
use cgmath::{Point2, Point3, Vector2, Vector3};
|
||||||
use galactica_util::constants::{
|
use galactica_content::Content;
|
||||||
STARFIELD_COUNT, STARFIELD_SIZE, STARFIELD_SIZE_MAX, STARFIELD_SIZE_MIN,
|
|
||||||
STARFIELD_SPRITE_INSTANCE_LIMIT, STARFIELD_Z_MAX, STARFIELD_Z_MIN, ZOOM_MAX,
|
|
||||||
};
|
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use crate::vertexbuffer::types::StarfieldInstance;
|
use crate::{
|
||||||
|
datastructs::RenderState,
|
||||||
|
vertexbuffer::{types::StarfieldInstance, BufferObject},
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct StarfieldStar {
|
pub(crate) struct StarfieldStar {
|
||||||
/// Star coordinates, in world space.
|
/// Star coordinates, in world space.
|
||||||
|
@ -30,22 +30,26 @@ impl Starfield {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
stars: Vec::new(),
|
stars: Vec::new(),
|
||||||
instance_count: 0u32,
|
instance_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn regenerate(&mut self) {
|
pub fn regenerate(&mut self, ct: &Content) {
|
||||||
// TODO: save seed in system, regenerate on jump
|
// TODO: save seed in system, regenerate on jump
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sz = STARFIELD_SIZE as f32 / 2.0;
|
let sz = ct.get_config().starfield_size as f32 / 2.0;
|
||||||
self.stars = (0..STARFIELD_COUNT)
|
self.stars = (0..ct.get_config().starfield_count)
|
||||||
.map(|_| StarfieldStar {
|
.map(|_| StarfieldStar {
|
||||||
pos: Point3 {
|
pos: Point3 {
|
||||||
x: rng.gen_range(-sz..=sz),
|
x: rng.gen_range(-sz..=sz),
|
||||||
y: rng.gen_range(-sz..=sz),
|
y: rng.gen_range(-sz..=sz),
|
||||||
z: rng.gen_range(STARFIELD_Z_MIN..STARFIELD_Z_MAX),
|
z: rng.gen_range(
|
||||||
|
ct.get_config().starfield_min_dist..=ct.get_config().starfield_max_dist,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
size: rng.gen_range(STARFIELD_SIZE_MIN..STARFIELD_SIZE_MAX),
|
size: rng.gen_range(
|
||||||
|
ct.get_config().starfield_min_size..ct.get_config().starfield_max_size,
|
||||||
|
),
|
||||||
tint: Vector2 {
|
tint: Vector2 {
|
||||||
x: rng.gen_range(0.0..=1.0),
|
x: rng.gen_range(0.0..=1.0),
|
||||||
y: rng.gen_range(0.0..=1.0),
|
y: rng.gen_range(0.0..=1.0),
|
||||||
|
@ -54,18 +58,18 @@ impl Starfield {
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_instances(&mut self, aspect: f32) -> Vec<StarfieldInstance> {
|
pub fn update_buffer(&mut self, ct: &Content, state: &mut RenderState) {
|
||||||
let sz = 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: Point2<i32> = {
|
||||||
// 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((aspect, 1.0)) * ZOOM_MAX;
|
let clip_nw = Point2::from((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 * STARFIELD_Z_MIN;
|
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<f32> = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into();
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -89,14 +93,14 @@ impl Starfield {
|
||||||
|
|
||||||
// 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) * STARFIELD_COUNT as i32)
|
while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * ct.get_config().starfield_count as i32)
|
||||||
> STARFIELD_SPRITE_INSTANCE_LIMIT as i32
|
> ct.get_config().starfield_instance_limit as i32
|
||||||
{
|
{
|
||||||
nw_tile -= Vector2::from((1, 1));
|
nw_tile -= Vector2::from((1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all tiles to buffer
|
// Add all tiles to buffer
|
||||||
let mut instances = Vec::new();
|
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 {
|
||||||
|
@ -105,21 +109,21 @@ impl Starfield {
|
||||||
z: 0.0,
|
z: 0.0,
|
||||||
};
|
};
|
||||||
for s in &self.stars {
|
for s in &self.stars {
|
||||||
instances.push(StarfieldInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.starfield.instances,
|
||||||
|
StarfieldInstance::SIZE * self.instance_count as u64,
|
||||||
|
bytemuck::cast_slice(&[StarfieldInstance {
|
||||||
position: (s.pos + offset).into(),
|
position: (s.pos + offset).into(),
|
||||||
size: s.size,
|
size: s.size,
|
||||||
tint: s.tint.into(),
|
tint: s.tint.into(),
|
||||||
})
|
}]),
|
||||||
}
|
);
|
||||||
}
|
self.instance_count += 1;
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce starfield limit
|
// instance_count is guaranteed to stay within buffer limits,
|
||||||
if instances.len() as u64 > STARFIELD_SPRITE_INSTANCE_LIMIT {
|
// this is guaranteed by previous checks.
|
||||||
unreachable!("Starfield limit exceeded!")
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.instance_count = instances.len() as u32;
|
|
||||||
|
|
||||||
return instances;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub(super) struct FpsIndicator {
|
||||||
|
|
||||||
impl FpsIndicator {
|
impl FpsIndicator {
|
||||||
pub fn new(state: &mut RenderState) -> Self {
|
pub fn new(state: &mut RenderState) -> Self {
|
||||||
let mut buffer = Buffer::new(&mut state.text_font_system, Metrics::new(12.0, 20.0));
|
let mut buffer = Buffer::new(&mut state.text_font_system, Metrics::new(15.0, 20.0));
|
||||||
buffer.set_size(
|
buffer.set_size(
|
||||||
&mut state.text_font_system,
|
&mut state.text_font_system,
|
||||||
state.window_size.width as f32,
|
state.window_size.width as f32,
|
||||||
|
@ -36,7 +36,7 @@ impl FpsIndicator {
|
||||||
self.buffer.set_text(
|
self.buffer.set_text(
|
||||||
&mut state.text_font_system,
|
&mut state.text_font_system,
|
||||||
&format!(
|
&format!(
|
||||||
"Frame: {:04.02?}%\nGame: {:04.02?}%\nShips: {:04.02?}%\nPhys: {:04.02?}%\nRender: {:.02?}",
|
"Frame: {:05.02?}%\nGame: {:05.02?}%\nShips: {:05.02?}%\nPhys: {:05.02?}%\nRender: {:.02?}",
|
||||||
100.0 * (input.timing.frame / input.timing.render),
|
100.0 * (input.timing.frame / input.timing.render),
|
||||||
100.0 * (input.timing.galaxy / input.timing.frame),
|
100.0 * (input.timing.galaxy / input.timing.frame),
|
||||||
100.0 * (input.timing.physics_sim / input.timing.frame),
|
100.0 * (input.timing.physics_sim / input.timing.frame),
|
||||||
|
|
|
@ -32,9 +32,9 @@ impl Radar {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let player_position = util::rigidbody_position(player_body);
|
let player_position = util::rigidbody_position(player_body);
|
||||||
let planet_sprite = input.content.get_sprite_handle("ui::planetblip");
|
let planet_sprite = input.ct.get_sprite_handle("ui::planetblip");
|
||||||
let ship_sprite = input.content.get_sprite_handle("ui::shipblip");
|
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
|
||||||
let arrow_sprite = input.content.get_sprite_handle("ui::centerarrow");
|
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
@ -52,13 +52,13 @@ impl Radar {
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
size: radar_size,
|
size: radar_size,
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
sprite_index: input.content.get_sprite_handle("ui::radar").get_index(),
|
sprite_index: input.ct.get_sprite_handle("ui::radar").get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
state.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
// Draw system objects
|
// Draw system objects
|
||||||
let system = input.content.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 {
|
||||||
|
@ -107,17 +107,17 @@ impl Radar {
|
||||||
for (s, r) in input.systemsim.iter_ship_body() {
|
for (s, r) in input.systemsim.iter_ship_body() {
|
||||||
// This will be None if this ship is dead.
|
// This will be None if this ship is dead.
|
||||||
// Stays around while the physics system runs a collapse sequence
|
// Stays around while the physics system runs a collapse sequence
|
||||||
let color = match input.data.get_ship(s.data_handle) {
|
let color = match input.gx.get_ship(s.data_handle) {
|
||||||
None => {
|
None => {
|
||||||
// TODO: configurable
|
// TODO: configurable
|
||||||
[0.2, 0.2, 0.2, 1.0]
|
[0.2, 0.2, 0.2, 1.0]
|
||||||
}
|
}
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let c = input.content.get_faction(data.get_faction()).color;
|
let c = input.ct.get_faction(data.get_faction()).color;
|
||||||
[c[0], c[1], c[2], 1.0]
|
[c[0], c[1], c[2], 1.0]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let ship = input.content.get_ship(s.data_handle.content_handle());
|
let ship = input.ct.get_ship(s.data_handle.content_handle());
|
||||||
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 = util::rigidbody_position(r);
|
||||||
let d = (p - player_position) / radar_range;
|
let d = (p - player_position) / radar_range;
|
||||||
|
@ -167,7 +167,7 @@ impl Radar {
|
||||||
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];
|
||||||
if m < 0.8 {
|
if m < 0.8 {
|
||||||
let sprite = input.content.get_sprite_handle("ui::radarframe");
|
let sprite = input.ct.get_sprite_handle("ui::radarframe");
|
||||||
let size = 7.0f32.min((0.8 - m) * 70.0);
|
let size = 7.0f32.min((0.8 - m) * 70.0);
|
||||||
|
|
||||||
// Enforce buffer limit (this section adds four items)
|
// Enforce buffer limit (this section adds four items)
|
||||||
|
|
|
@ -27,14 +27,11 @@ impl Status {
|
||||||
|
|
||||||
let player_world_object = input.systemsim.get_ship(input.player_data).unwrap();
|
let player_world_object = input.systemsim.get_ship(input.player_data).unwrap();
|
||||||
|
|
||||||
let data = input
|
let data = input.gx.get_ship(player_world_object.data_handle).unwrap();
|
||||||
.data
|
|
||||||
.get_ship(player_world_object.data_handle)
|
|
||||||
.unwrap();
|
|
||||||
let max_shields = data.get_outfits().get_shield_strength();
|
let max_shields = data.get_outfits().get_shield_strength();
|
||||||
let current_shields = data.get_shields();
|
let current_shields = data.get_shields();
|
||||||
let current_hull = data.get_hull();
|
let current_hull = data.get_hull();
|
||||||
let max_hull = input.content.get_ship(data.get_content()).hull;
|
let max_hull = input.ct.get_ship(data.get_content()).hull;
|
||||||
|
|
||||||
state.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&state.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
|
@ -45,7 +42,7 @@ impl Status {
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
size: 200.0,
|
size: 200.0,
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
sprite_index: input.content.get_sprite_handle("ui::status").get_index(),
|
sprite_index: input.ct.get_sprite_handle("ui::status").get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
state.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
|
@ -2,48 +2,6 @@
|
||||||
|
|
||||||
// TODO: many of these should be moved to a config file or cli option
|
// TODO: many of these should be moved to a config file or cli option
|
||||||
|
|
||||||
/// Minimum zoom level
|
|
||||||
pub const ZOOM_MIN: f32 = 200.0;
|
|
||||||
/// Maximum zoom level
|
|
||||||
pub const ZOOM_MAX: f32 = 2000.0;
|
|
||||||
|
|
||||||
/// Z-axis range for starfield stars
|
|
||||||
/// This does not affect scale.
|
|
||||||
pub const STARFIELD_Z_MIN: f32 = 75.0;
|
|
||||||
/// Z-axis range for starfield stars
|
|
||||||
/// This does not affect scale.
|
|
||||||
pub const STARFIELD_Z_MAX: f32 = 200.0;
|
|
||||||
|
|
||||||
/// Size range for starfield stars, in game units.
|
|
||||||
/// This is scaled for zoom, but NOT for distance.
|
|
||||||
pub const STARFIELD_SIZE_MIN: f32 = 0.2;
|
|
||||||
/// Size range for starfield stars, in game units.
|
|
||||||
/// This is scaled for zoom, but NOT for distance.
|
|
||||||
pub const STARFIELD_SIZE_MAX: f32 = 1.8;
|
|
||||||
|
|
||||||
/// Size of a square starfield tile, in game units.
|
|
||||||
/// A tile of size STARFIELD_Z_MAX * screen-size-in-game-units
|
|
||||||
/// will completely cover a (square) screen. This depends on zoom!
|
|
||||||
///
|
|
||||||
/// Use a value smaller than zoom_max for debug.
|
|
||||||
pub const STARFIELD_SIZE: u64 = STARFIELD_Z_MAX as u64 * ZOOM_MAX as u64;
|
|
||||||
|
|
||||||
/// Average number of stars per game unit
|
|
||||||
pub const STARFIELD_DENSITY: f64 = 0.01;
|
|
||||||
|
|
||||||
/// Number of stars in one starfield tile
|
|
||||||
/// Must fit inside an i32
|
|
||||||
pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64;
|
|
||||||
|
|
||||||
/// Name of starfield sprite
|
|
||||||
pub const STARFIELD_SPRITE_NAME: &'static str = "starfield";
|
|
||||||
|
|
||||||
/// Root directory of game content
|
|
||||||
pub const CONTENT_ROOT: &'static str = "./content";
|
|
||||||
|
|
||||||
/// Root directory of game images
|
|
||||||
pub const IMAGE_ROOT: &'static str = "./assets/render";
|
|
||||||
|
|
||||||
/// We can draw at most this many object sprites on the screen.
|
/// We can draw at most this many object sprites on the screen.
|
||||||
pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500;
|
pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500;
|
||||||
|
|
||||||
|
@ -57,9 +15,6 @@ pub const RADIALBAR_SPRITE_INSTANCE_LIMIT: u64 = 10;
|
||||||
/// The size of our circular particle buffer. When we create particles, the oldest ones are replaced.
|
/// The size of our circular particle buffer. When we create particles, the oldest ones are replaced.
|
||||||
pub const PARTICLE_SPRITE_INSTANCE_LIMIT: u64 = 1000;
|
pub const PARTICLE_SPRITE_INSTANCE_LIMIT: u64 = 1000;
|
||||||
|
|
||||||
/// Must be small enough to fit in an i32
|
|
||||||
pub const STARFIELD_SPRITE_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;
|
|
||||||
|
|
||||||
/// The maximum number of sprites we can define
|
/// The maximum number of sprites we can define
|
||||||
pub const SPRITE_LIMIT: u32 = 1024;
|
pub const SPRITE_LIMIT: u32 = 1024;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue