Compare commits
7 Commits
b48f5e71df
...
b1de7f37ff
Author | SHA1 | Date |
---|---|---|
Mark | b1de7f37ff | |
Mark | 975d2e6590 | |
Mark | fc728eaf0e | |
Mark | fa8a0f5761 | |
Mark | 8fbbf7f110 | |
Mark | 4f6a1b4bf7 | |
Mark | 48d1fc3093 |
|
@ -578,10 +578,10 @@ name = "galactica"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytemuck",
|
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"galactica-content",
|
"galactica-content",
|
||||||
|
"galactica-render",
|
||||||
"image",
|
"image",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
"pollster",
|
"pollster",
|
||||||
|
@ -605,6 +605,19 @@ dependencies = [
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "galactica-render"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bytemuck",
|
||||||
|
"cgmath",
|
||||||
|
"galactica-content",
|
||||||
|
"image",
|
||||||
|
"wgpu",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
|
|
@ -28,26 +28,27 @@ rpath = false
|
||||||
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/content"]
|
members = ["crates/content", "crates/render"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Internal crates
|
# Internal crates
|
||||||
galactica-content = { path = "crates/content" }
|
galactica-content = { path = "crates/content" }
|
||||||
|
galactica-render = { path = "crates/render" }
|
||||||
|
|
||||||
# Files
|
# Files
|
||||||
image = { version = "0.24", features = ["png"] }
|
image = { version = "0.24", features = ["png"] }
|
||||||
# Graphics
|
# Graphics
|
||||||
winit = "0.28"
|
winit = "0.28"
|
||||||
wgpu = "0.18"
|
wgpu = "0.18"
|
||||||
bytemuck = { version = "1.12", features = ["derive"] }
|
|
||||||
# Physics
|
# Physics
|
||||||
rapier2d = { version = "0.17.2" } #, features = ["parallel"] }
|
rapier2d = { version = "0.17.2" }
|
||||||
nalgebra = "0.32.3"
|
nalgebra = "0.32.3"
|
||||||
crossbeam = "0.8.3"
|
crossbeam = "0.8.3"
|
||||||
# Misc helpers
|
# Misc helpers
|
||||||
pollster = "0.3"
|
pollster = "0.3"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
# TODO: migrate to nalgebra
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
walkdir = "2.4.0"
|
walkdir = "2.4.0"
|
||||||
|
|
10
TODO.md
10
TODO.md
|
@ -64,7 +64,10 @@
|
||||||
- Pause game
|
- Pause game
|
||||||
- Better player controller? (only one shipbehavior needs inputs)
|
- Better player controller? (only one shipbehavior needs inputs)
|
||||||
- Clear all `// TODO:` comments littered in the source
|
- Clear all `// TODO:` comments littered in the source
|
||||||
- Config file
|
- CLI options (debug, save location, content location, check content)
|
||||||
|
- Config file and compile options, remove all those consts.
|
||||||
|
- Engine flares shouldn't be centered
|
||||||
|
- Sprite optimization: do we need to allocate a new `Vec` every frame? Probably not.
|
||||||
|
|
||||||
## Content
|
## Content
|
||||||
- Angled engines
|
- Angled engines
|
||||||
|
@ -92,7 +95,6 @@
|
||||||
- Lookahead -> position or direction?
|
- Lookahead -> position or direction?
|
||||||
- Damping?
|
- Damping?
|
||||||
- Important objects affect camera
|
- Important objects affect camera
|
||||||
-
|
|
||||||
|
|
||||||
## Visuals
|
## Visuals
|
||||||
- Particles
|
- Particles
|
||||||
|
@ -101,6 +103,7 @@
|
||||||
- Zoom parallax (?)
|
- Zoom parallax (?)
|
||||||
- Background haze
|
- Background haze
|
||||||
- Nova dust parallax
|
- Nova dust parallax
|
||||||
|
- Ship outlines in radar
|
||||||
|
|
||||||
|
|
||||||
## Write and Document
|
## Write and Document
|
||||||
|
@ -130,4 +133,5 @@
|
||||||
- More interesting trading?
|
- More interesting trading?
|
||||||
- Death penalty
|
- Death penalty
|
||||||
- Find your wreckage when you die (dark souls/HK)
|
- Find your wreckage when you die (dark souls/HK)
|
||||||
- Lose some outfits, lose ship? Real risk for going out! (HK does this well)
|
- Lose some outfits, lose ship? Real risk for going out! (HK does this well)
|
||||||
|
- Damage to ship subsystems
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 711e4fd58100ee31e41c3c27273f7caa706d8d91
|
Subproject commit 8205d79e8aa9d7e4976fe8a7794e83819ec19688
|
|
@ -18,3 +18,9 @@ path = "projectile/blaster.png"
|
||||||
|
|
||||||
[texture."ship::gypsum"]
|
[texture."ship::gypsum"]
|
||||||
path = "ship/gypsum.png"
|
path = "ship/gypsum.png"
|
||||||
|
|
||||||
|
[texture."ui::radar"]
|
||||||
|
path = "ui/radar.png"
|
||||||
|
|
||||||
|
[texture."ui::blip"]
|
||||||
|
path = "ui/blip.png"
|
||||||
|
|
|
@ -209,6 +209,12 @@ impl Content {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a texture from a handle
|
||||||
|
pub fn get_texture_handle(&self, name: &str) -> TextureHandle {
|
||||||
|
return *self.texture_index.get(name).unwrap();
|
||||||
|
// TODO: handle errors
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a texture from a handle
|
/// Get a texture from a handle
|
||||||
pub fn get_texture(&self, h: TextureHandle) -> &Texture {
|
pub fn get_texture(&self, h: TextureHandle) -> &Texture {
|
||||||
// In theory, this could fail if h has a bad index, but that shouldn't ever happen.
|
// In theory, this could fail if h has a bad index, but that shouldn't ever happen.
|
||||||
|
|
|
@ -5,10 +5,10 @@ use cgmath::Deg;
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
use crate::{handle::TextureHandle, Content};
|
||||||
|
|
||||||
use super::OutfitSpace;
|
use crate::OutfitSpace;
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use super::super::shared;
|
use crate::part::shared;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
// These are never seen by code outside this crate.
|
// These are never seen by code outside this crate.
|
||||||
|
|
|
@ -2,12 +2,10 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
use crate::{handle::TextureHandle, Content, OutfitSpace};
|
||||||
|
|
||||||
use super::OutfitSpace;
|
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use super::super::shared;
|
use crate::part::shared;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
// These are never seen by code outside this crate.
|
// These are never seen by code outside this crate.
|
||||||
|
|
|
@ -4,12 +4,10 @@ use anyhow::{bail, Result};
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use nalgebra::{point, Point};
|
use nalgebra::{point, Point};
|
||||||
|
|
||||||
use crate::{handle::TextureHandle, Content};
|
use crate::{handle::TextureHandle, Content, OutfitSpace};
|
||||||
|
|
||||||
use super::OutfitSpace;
|
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use super::super::shared;
|
use crate::part::shared;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
|
|
|
@ -5,8 +5,8 @@ use std::collections::{HashMap, HashSet};
|
||||||
use crate::{handle::TextureHandle, util::Polar, Content};
|
use crate::{handle::TextureHandle, util::Polar, Content};
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use super::HashMap;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
// These are never seen by code outside this crate.
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "galactica-render"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
galactica-content = { path = "../content" }
|
||||||
|
|
||||||
|
# Misc helpers
|
||||||
|
anyhow = "1.0"
|
||||||
|
cgmath = "0.18.0"
|
||||||
|
# Files
|
||||||
|
image = { version = "0.24", features = ["png"] }
|
||||||
|
# Graphics
|
||||||
|
winit = "0.28"
|
||||||
|
wgpu = "0.18"
|
||||||
|
bytemuck = { version = "1.12", features = ["derive"] }
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::consts;
|
use crate::consts_main;
|
||||||
use cgmath::Matrix4;
|
use cgmath::Matrix4;
|
||||||
|
|
||||||
// We can draw at most this many sprites on the screen.
|
// We can draw at most this many sprites on the screen.
|
||||||
|
@ -6,7 +6,7 @@ use cgmath::Matrix4;
|
||||||
pub const SPRITE_INSTANCE_LIMIT: u64 = 500;
|
pub const SPRITE_INSTANCE_LIMIT: u64 = 500;
|
||||||
|
|
||||||
// Must be small enough to fit in an i32
|
// Must be small enough to fit in an i32
|
||||||
pub const STARFIELD_INSTANCE_LIMIT: u64 = consts::STARFIELD_COUNT * 24;
|
pub const STARFIELD_INSTANCE_LIMIT: u64 = consts_main::STARFIELD_COUNT * 24;
|
||||||
|
|
||||||
/// Shader entry points
|
/// Shader entry points
|
||||||
pub const SHADER_MAIN_VERTEX: &'static str = "vertex_main";
|
pub const SHADER_MAIN_VERTEX: &'static str = "vertex_main";
|
|
@ -0,0 +1,26 @@
|
||||||
|
pub const ZOOM_MIN: f32 = 200.0;
|
||||||
|
pub const ZOOM_MAX: f32 = 2000.0;
|
||||||
|
|
||||||
|
/// Z-axis range for starfield stars
|
||||||
|
/// This does not affect scale.
|
||||||
|
pub const STARFIELD_Z_MIN: f32 = 100.0;
|
||||||
|
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;
|
||||||
|
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;
|
|
@ -1,34 +1,39 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector2, Vector3};
|
use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector2, Vector3};
|
||||||
use galactica_content::Content;
|
|
||||||
use std::{iter, rc::Rc};
|
use std::{iter, rc::Rc};
|
||||||
use wgpu;
|
use wgpu;
|
||||||
use winit::{self, dpi::PhysicalSize, window::Window};
|
use winit::{self, dpi::LogicalSize, window::Window};
|
||||||
|
|
||||||
use super::{
|
use crate::{
|
||||||
consts::{OPENGL_TO_WGPU_MATRIX, SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT},
|
consts::{OPENGL_TO_WGPU_MATRIX, SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT},
|
||||||
|
consts_main, content,
|
||||||
globaldata::{GlobalData, GlobalDataContent},
|
globaldata::{GlobalData, GlobalDataContent},
|
||||||
pipeline::PipelineBuilder,
|
pipeline::PipelineBuilder,
|
||||||
sprite::SubSprite,
|
sprite::ObjectSubSprite,
|
||||||
texturearray::TextureArray,
|
texturearray::TextureArray,
|
||||||
vertexbuffer::{
|
vertexbuffer::{
|
||||||
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
||||||
types::{SpriteInstance, StarfieldInstance, TexturedVertex},
|
types::{SpriteInstance, StarfieldInstance, TexturedVertex},
|
||||||
VertexBuffer,
|
VertexBuffer,
|
||||||
},
|
},
|
||||||
Sprite,
|
ObjectSprite, StarfieldStar, UiSprite,
|
||||||
};
|
};
|
||||||
use crate::{consts, game::Game};
|
|
||||||
|
|
||||||
|
/// A high-level GPU wrapper. Consumes game state,
|
||||||
|
/// produces pretty pictures.
|
||||||
pub struct GPUState {
|
pub struct GPUState {
|
||||||
|
/// The window to we draw on
|
||||||
|
pub window: Window,
|
||||||
|
|
||||||
|
/// The size of the window we draw on
|
||||||
|
pub window_size: winit::dpi::PhysicalSize<u32>,
|
||||||
|
|
||||||
device: wgpu::Device,
|
device: wgpu::Device,
|
||||||
config: wgpu::SurfaceConfiguration,
|
config: wgpu::SurfaceConfiguration,
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface,
|
||||||
queue: wgpu::Queue,
|
queue: wgpu::Queue,
|
||||||
|
|
||||||
pub window: Window,
|
|
||||||
pub window_size: winit::dpi::PhysicalSize<u32>,
|
|
||||||
window_aspect: f32,
|
window_aspect: f32,
|
||||||
|
|
||||||
sprite_pipeline: wgpu::RenderPipeline,
|
sprite_pipeline: wgpu::RenderPipeline,
|
||||||
|
@ -46,7 +51,8 @@ struct VertexBuffers {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
pub async fn new(window: Window, ct: &Content) -> Result<Self> {
|
/// Make a new GPUState that draws on `window`
|
||||||
|
pub async fn new(window: Window, ct: &content::Content) -> Result<Self> {
|
||||||
let window_size = window.inner_size();
|
let window_size = window.inner_size();
|
||||||
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
||||||
|
|
||||||
|
@ -141,7 +147,7 @@ impl GPUState {
|
||||||
let sprite_pipeline = PipelineBuilder::new("sprite", &device)
|
let sprite_pipeline = PipelineBuilder::new("sprite", &device)
|
||||||
.set_shader(include_str!(concat!(
|
.set_shader(include_str!(concat!(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
"/src/render/shaders/",
|
"/shaders/",
|
||||||
"sprite.wgsl"
|
"sprite.wgsl"
|
||||||
)))
|
)))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
|
@ -153,7 +159,7 @@ impl GPUState {
|
||||||
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
||||||
.set_shader(include_str!(concat!(
|
.set_shader(include_str!(concat!(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
"/src/render/shaders/",
|
"/shaders/",
|
||||||
"starfield.wgsl"
|
"starfield.wgsl"
|
||||||
)))
|
)))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
|
@ -182,11 +188,15 @@ impl GPUState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the window this GPUState is attached to
|
||||||
pub fn window(&self) -> &Window {
|
pub fn window(&self) -> &Window {
|
||||||
&self.window
|
&self.window
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, game: &Game, new_size: PhysicalSize<u32>) {
|
/// Update window size.
|
||||||
|
/// This should be called whenever our window is resized.
|
||||||
|
pub fn resize(&mut self, starfield: &Vec<StarfieldStar>) {
|
||||||
|
let new_size = self.window.inner_size();
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
self.window_size = new_size;
|
self.window_size = new_size;
|
||||||
self.window_aspect = new_size.width as f32 / new_size.height as f32;
|
self.window_aspect = new_size.width as f32 / new_size.height as f32;
|
||||||
|
@ -194,18 +204,19 @@ 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(game)
|
self.update_starfield_buffer(starfield)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a SpriteInstance for s and add it to instances.
|
/// Create a SpriteInstance for an object and add it to `instances`.
|
||||||
/// Also handles child sprites.
|
/// Also handles child sprites.
|
||||||
fn push_sprite(
|
fn push_object_sprite(
|
||||||
&self,
|
&self,
|
||||||
game: &Game,
|
camera_zoom: f32,
|
||||||
|
camera_pos: Point2<f32>,
|
||||||
instances: &mut Vec<SpriteInstance>,
|
instances: &mut Vec<SpriteInstance>,
|
||||||
clip_ne: Point2<f32>,
|
clip_ne: Point2<f32>,
|
||||||
clip_sw: Point2<f32>,
|
clip_sw: Point2<f32>,
|
||||||
s: Sprite,
|
s: &ObjectSprite,
|
||||||
) {
|
) {
|
||||||
// Position adjusted for parallax
|
// Position adjusted for parallax
|
||||||
// TODO: adjust parallax for zoom?
|
// TODO: adjust parallax for zoom?
|
||||||
|
@ -213,7 +224,7 @@ impl GPUState {
|
||||||
(Point2 {
|
(Point2 {
|
||||||
x: s.pos.x,
|
x: s.pos.x,
|
||||||
y: s.pos.y,
|
y: s.pos.y,
|
||||||
} - game.camera.pos.to_vec())
|
} - camera_pos.to_vec())
|
||||||
/ s.pos.z
|
/ s.pos.z
|
||||||
};
|
};
|
||||||
let texture = self.texture_array.get_texture(s.texture);
|
let texture = self.texture_array.get_texture(s.texture);
|
||||||
|
@ -237,7 +248,7 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: clean up
|
// TODO: clean up
|
||||||
let scale = height / game.camera.zoom;
|
let scale = height / camera_zoom;
|
||||||
|
|
||||||
// Note that our mesh starts centered at (0, 0).
|
// Note that our mesh starts centered at (0, 0).
|
||||||
// This is essential---we do not want scale and rotation
|
// This is essential---we do not want scale and rotation
|
||||||
|
@ -266,8 +277,8 @@ impl GPUState {
|
||||||
// The height of the viewport is `zoom` in game units,
|
// The height of the viewport is `zoom` in game units,
|
||||||
// but it's 2 in screen units! (since coordinates range from -1 to 1)
|
// but it's 2 in screen units! (since coordinates range from -1 to 1)
|
||||||
let translate = Matrix4::from_translation(Vector3 {
|
let translate = Matrix4::from_translation(Vector3 {
|
||||||
x: pos.x / (game.camera.zoom / 2.0) / self.window_aspect,
|
x: pos.x / (camera_zoom / 2.0) / self.window_aspect,
|
||||||
y: pos.y / (game.camera.zoom / 2.0),
|
y: pos.y / (camera_zoom / 2.0),
|
||||||
z: 0.0,
|
z: 0.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -282,40 +293,40 @@ impl GPUState {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add children
|
// Add children
|
||||||
if let Some(children) = s.children {
|
if let Some(children) = &s.children {
|
||||||
for c in children {
|
for c in children {
|
||||||
self.push_subsprite(game, instances, c, pos, s.angle);
|
self.push_object_subsprite(camera_zoom, instances, c, pos, s.angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a sprite's subsprite to instance.
|
/// Add an object sprite's subsprite to `instances`.
|
||||||
/// Only called by push_sprite.
|
/// Only called by `self.push_object_sprite`.
|
||||||
fn push_subsprite(
|
fn push_object_subsprite(
|
||||||
&self,
|
&self,
|
||||||
game: &Game,
|
camera_zoom: f32,
|
||||||
instances: &mut Vec<SpriteInstance>,
|
instances: &mut Vec<SpriteInstance>,
|
||||||
s: SubSprite,
|
s: &ObjectSubSprite,
|
||||||
parent_pos: Point2<f32>,
|
parent_pos: Point2<f32>,
|
||||||
parent_angle: Deg<f32>,
|
parent_angle: Deg<f32>,
|
||||||
) {
|
) {
|
||||||
let texture = self.texture_array.get_texture(s.texture);
|
let texture = self.texture_array.get_texture(s.texture);
|
||||||
let scale = s.size / (s.pos.z * game.camera.zoom);
|
let scale = s.size / (s.pos.z * camera_zoom);
|
||||||
let sprite_aspect_and_scale =
|
let sprite_aspect_and_scale =
|
||||||
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
||||||
let rotate = Matrix4::from_angle_z(s.angle);
|
let rotate = Matrix4::from_angle_z(s.angle);
|
||||||
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
|
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
|
||||||
|
|
||||||
let ptranslate = Matrix4::from_translation(Vector3 {
|
let ptranslate = Matrix4::from_translation(Vector3 {
|
||||||
x: parent_pos.x / (game.camera.zoom / 2.0) / self.window_aspect,
|
x: parent_pos.x / (camera_zoom / 2.0) / self.window_aspect,
|
||||||
y: parent_pos.y / (game.camera.zoom / 2.0),
|
y: parent_pos.y / (camera_zoom / 2.0),
|
||||||
z: 0.0,
|
z: 0.0,
|
||||||
});
|
});
|
||||||
let protate = Matrix4::from_angle_z(parent_angle);
|
let protate = Matrix4::from_angle_z(parent_angle);
|
||||||
|
|
||||||
let translate = Matrix4::from_translation(Vector3 {
|
let translate = Matrix4::from_translation(Vector3 {
|
||||||
x: s.pos.x / (game.camera.zoom / 2.0) / self.window_aspect,
|
x: s.pos.x / (camera_zoom / 2.0) / self.window_aspect,
|
||||||
y: s.pos.y / (game.camera.zoom / 2.0),
|
y: s.pos.y / (camera_zoom / 2.0),
|
||||||
z: 0.0,
|
z: 0.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -332,20 +343,65 @@ impl GPUState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a SpriteInstance for a ui sprite and add it to `instances`
|
||||||
|
fn push_ui_sprite(&self, instances: &mut Vec<SpriteInstance>, s: &UiSprite) {
|
||||||
|
let logical_size: LogicalSize<f32> =
|
||||||
|
self.window_size.to_logical(self.window.scale_factor());
|
||||||
|
|
||||||
|
let texture = self.texture_array.get_texture(s.texture);
|
||||||
|
let width = s.dimensions.x;
|
||||||
|
let height = s.dimensions.y;
|
||||||
|
|
||||||
|
let scale = Matrix4::from_nonuniform_scale(
|
||||||
|
width / logical_size.width,
|
||||||
|
height / logical_size.height,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
let rotate = Matrix4::from_angle_z(s.angle);
|
||||||
|
let translate = Matrix4::from_translation(match s.pos {
|
||||||
|
super::AnchoredUiPosition::NorthWest(p) => Vector3 {
|
||||||
|
// Note the signs. Positive y points north!
|
||||||
|
x: -1.0 + (width / 2.0 + p.x) / (logical_size.width / 2.0),
|
||||||
|
y: 1.0 - (height / 2.0 - p.y) / (logical_size.height / 2.0),
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
_ => Vector3 {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
instances.push(SpriteInstance {
|
||||||
|
transform: (OPENGL_TO_WGPU_MATRIX * translate * rotate * scale).into(),
|
||||||
|
texture_index: texture.index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Make a SpriteInstance for each of the game's visible sprites.
|
/// Make a SpriteInstance for each of the game's visible sprites.
|
||||||
/// Will panic if SPRITE_INSTANCE_LIMIT is exceeded.
|
/// Will panic if SPRITE_INSTANCE_LIMIT is exceeded.
|
||||||
///
|
///
|
||||||
/// This is only called inside self.render()
|
/// This is only called inside self.render()
|
||||||
fn make_sprite_instances(&self, game: &Game) -> Vec<SpriteInstance> {
|
fn make_sprite_instances(
|
||||||
|
&self,
|
||||||
|
camera_zoom: f32,
|
||||||
|
camera_pos: Point2<f32>,
|
||||||
|
objects: &Vec<ObjectSprite>,
|
||||||
|
ui: &Vec<UiSprite>,
|
||||||
|
) -> Vec<SpriteInstance> {
|
||||||
let mut instances: Vec<SpriteInstance> = Vec::new();
|
let mut instances: Vec<SpriteInstance> = Vec::new();
|
||||||
|
|
||||||
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
||||||
// Used to skip off-screen sprites.
|
// Used to skip off-screen sprites.
|
||||||
let clip_ne = Point2::from((-self.window_aspect, 1.0)) * game.camera.zoom;
|
let clip_ne = Point2::from((-self.window_aspect, 1.0)) * camera_zoom;
|
||||||
let clip_sw = Point2::from((self.window_aspect, -1.0)) * game.camera.zoom;
|
let clip_sw = Point2::from((self.window_aspect, -1.0)) * camera_zoom;
|
||||||
|
|
||||||
for s in game.get_sprites() {
|
for s in objects {
|
||||||
self.push_sprite(game, &mut instances, clip_ne, clip_sw, s);
|
self.push_object_sprite(camera_zoom, camera_pos, &mut instances, clip_ne, clip_sw, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
for s in ui {
|
||||||
|
self.push_ui_sprite(&mut instances, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce sprite limit
|
// Enforce sprite limit
|
||||||
|
@ -361,18 +417,19 @@ impl GPUState {
|
||||||
/// Will panic if STARFIELD_INSTANCE_LIMIT is exceeded.
|
/// Will panic if STARFIELD_INSTANCE_LIMIT is exceeded.
|
||||||
///
|
///
|
||||||
/// Starfield data rarely changes, so this is called only when it's needed.
|
/// Starfield data rarely changes, so this is called only when it's needed.
|
||||||
pub fn update_starfield_buffer(&mut self, game: &Game) {
|
pub fn update_starfield_buffer(&mut self, starfield: &Vec<StarfieldStar>) {
|
||||||
let sz = consts::STARFIELD_SIZE as f32;
|
let sz = consts_main::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((self.window_aspect, 1.0)) * consts::ZOOM_MAX;
|
// TODO: we probably don't need to re-compute this on resize
|
||||||
|
let clip_nw = Point2::from((self.window_aspect, 1.0)) * consts_main::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 * consts::STARFIELD_Z_MIN;
|
let v: Point2<f32> = clip_nw * consts_main::STARFIELD_Z_MIN;
|
||||||
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]
|
||||||
|
@ -396,7 +453,7 @@ impl GPUState {
|
||||||
|
|
||||||
// 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) * consts::STARFIELD_COUNT as i32)
|
while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * consts_main::STARFIELD_COUNT as i32)
|
||||||
> STARFIELD_INSTANCE_LIMIT as i32
|
> STARFIELD_INSTANCE_LIMIT as i32
|
||||||
{
|
{
|
||||||
nw_tile -= Vector2::from((1, 1));
|
nw_tile -= Vector2::from((1, 1));
|
||||||
|
@ -411,7 +468,7 @@ impl GPUState {
|
||||||
y: sz * y as f32,
|
y: sz * y as f32,
|
||||||
z: 0.0,
|
z: 0.0,
|
||||||
};
|
};
|
||||||
for s in &game.system.starfield {
|
for s in starfield {
|
||||||
instances.push(StarfieldInstance {
|
instances.push(StarfieldInstance {
|
||||||
position: (s.pos + offset).into(),
|
position: (s.pos + offset).into(),
|
||||||
size: s.size,
|
size: s.size,
|
||||||
|
@ -434,7 +491,14 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, game: &Game) -> Result<(), wgpu::SurfaceError> {
|
/// Main render function. Draws sprites on a window.
|
||||||
|
pub fn render(
|
||||||
|
&mut self,
|
||||||
|
camera_pos: Point2<f32>,
|
||||||
|
camera_zoom: f32,
|
||||||
|
object_sprites: &Vec<ObjectSprite>,
|
||||||
|
ui_sprites: &Vec<UiSprite>,
|
||||||
|
) -> Result<(), wgpu::SurfaceError> {
|
||||||
let output = self.surface.get_current_texture()?;
|
let output = self.surface.get_current_texture()?;
|
||||||
let view = output
|
let view = output
|
||||||
.texture
|
.texture
|
||||||
|
@ -472,22 +536,26 @@ impl GPUState {
|
||||||
&self.global_data.buffer,
|
&self.global_data.buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[GlobalDataContent {
|
bytemuck::cast_slice(&[GlobalDataContent {
|
||||||
camera_position: game.camera.pos.into(),
|
camera_position: camera_pos.into(),
|
||||||
camera_zoom: [game.camera.zoom, 0.0],
|
camera_zoom: [camera_zoom, 0.0],
|
||||||
camera_zoom_limits: [consts::ZOOM_MIN, consts::ZOOM_MAX],
|
camera_zoom_limits: [consts_main::ZOOM_MIN, consts_main::ZOOM_MAX],
|
||||||
window_size: [
|
window_size: [
|
||||||
self.window_size.width as f32,
|
self.window_size.width as f32,
|
||||||
self.window_size.height as f32,
|
self.window_size.height as f32,
|
||||||
],
|
],
|
||||||
window_aspect: [self.window_aspect, 0.0],
|
window_aspect: [self.window_aspect, 0.0],
|
||||||
starfield_texture: [self.texture_array.get_starfield_texture().index, 0],
|
starfield_texture: [self.texture_array.get_starfield_texture().index, 0],
|
||||||
starfield_tile_size: [consts::STARFIELD_SIZE as f32, 0.0],
|
starfield_tile_size: [consts_main::STARFIELD_SIZE as f32, 0.0],
|
||||||
starfield_size_limits: [consts::STARFIELD_SIZE_MIN, consts::STARFIELD_SIZE_MAX],
|
starfield_size_limits: [
|
||||||
|
consts_main::STARFIELD_SIZE_MIN,
|
||||||
|
consts_main::STARFIELD_SIZE_MAX,
|
||||||
|
],
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create sprite instances
|
// Create sprite instances
|
||||||
let sprite_instances = self.make_sprite_instances(game);
|
let sprite_instances =
|
||||||
|
self.make_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites);
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.vertex_buffers.sprite.instances,
|
&self.vertex_buffers.sprite.instances,
|
||||||
0,
|
0,
|
|
@ -0,0 +1,39 @@
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
//! This crate contains all drawing logic and NO game logic.
|
||||||
|
//! It converts game state to a nice picture.
|
||||||
|
//!
|
||||||
|
//! [`GPUState`] is the main struct this crate provides,
|
||||||
|
//! and the only one external code should interact with.
|
||||||
|
//! (Excluding data structs, like [`ObjectSprite`])
|
||||||
|
|
||||||
|
mod consts;
|
||||||
|
mod globaldata;
|
||||||
|
mod gpustate;
|
||||||
|
mod pipeline;
|
||||||
|
mod sprite;
|
||||||
|
mod texturearray;
|
||||||
|
mod vertexbuffer;
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
|
mod consts_main;
|
||||||
|
|
||||||
|
use cgmath::{Point3, Vector2};
|
||||||
|
use galactica_content as content;
|
||||||
|
pub use gpustate::GPUState;
|
||||||
|
pub use sprite::{AnchoredUiPosition, ObjectSprite, ObjectSubSprite, UiSprite};
|
||||||
|
|
||||||
|
/// TODO: this shouldn't be here
|
||||||
|
pub struct StarfieldStar {
|
||||||
|
/// Star coordinates, in world space.
|
||||||
|
/// These are relative to the center of a starfield tile.
|
||||||
|
pub pos: Point3<f32>,
|
||||||
|
|
||||||
|
/// Height in game units.
|
||||||
|
/// Will be scaled for zoom, but not for distance.
|
||||||
|
pub size: f32,
|
||||||
|
|
||||||
|
/// Color/brightness variation. Random between 0 and 1.
|
||||||
|
/// Used in starfield shader.
|
||||||
|
pub tint: Vector2<f32>,
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
use super::consts::{SHADER_MAIN_FRAGMENT, SHADER_MAIN_VERTEX};
|
use crate::consts::{SHADER_MAIN_FRAGMENT, SHADER_MAIN_VERTEX};
|
||||||
use super::vertexbuffer::VertexBuffer;
|
use crate::vertexbuffer::VertexBuffer;
|
||||||
|
|
||||||
pub struct PipelineBuilder<'a> {
|
pub struct PipelineBuilder<'a> {
|
||||||
// These are provided with new()
|
// These are provided with new()
|
|
@ -0,0 +1,78 @@
|
||||||
|
use crate::content;
|
||||||
|
use cgmath::{Deg, Point2, Point3};
|
||||||
|
|
||||||
|
/// The location of a UI element, in one of a few
|
||||||
|
/// possible coordinate systems.
|
||||||
|
///
|
||||||
|
/// Positive Y always points up,
|
||||||
|
/// positive X always points right.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum AnchoredUiPosition {
|
||||||
|
/// Position of this sprite's nw corner,
|
||||||
|
/// relative to the nw corner of the window.
|
||||||
|
NorthWest(Point2<f32>),
|
||||||
|
|
||||||
|
/// Position of this sprite's sw corner,
|
||||||
|
/// relative to the sw corner of the window.
|
||||||
|
SouthWest(Point2<f32>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A sprite that represents a ui element
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct UiSprite {
|
||||||
|
/// The texture to use for this sprite
|
||||||
|
pub texture: content::TextureHandle,
|
||||||
|
|
||||||
|
/// This object's position, in logical (dpi-adjusted) pixels
|
||||||
|
pub pos: AnchoredUiPosition,
|
||||||
|
|
||||||
|
/// The size of this sprite, in logical (dpi-adjusted) pixels
|
||||||
|
pub dimensions: Point2<f32>,
|
||||||
|
|
||||||
|
/// This sprite's rotation, measured ccw
|
||||||
|
pub angle: Deg<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A sprite that represents a world object:
|
||||||
|
/// Ships, planets, debris, etc
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ObjectSprite {
|
||||||
|
/// The texture to use for this sprite
|
||||||
|
pub texture: content::TextureHandle,
|
||||||
|
|
||||||
|
/// This object's center, in world coordinates.
|
||||||
|
pub pos: Point3<f32>,
|
||||||
|
|
||||||
|
/// The size of this sprite,
|
||||||
|
/// given as height in world units.
|
||||||
|
pub size: f32,
|
||||||
|
|
||||||
|
/// This sprite's rotation
|
||||||
|
/// (relative to north, measured ccw)
|
||||||
|
/// Note that this is different from the angle used by our physics system.
|
||||||
|
pub angle: Deg<f32>,
|
||||||
|
|
||||||
|
/// Sprites that should be drawn relative to this sprite.
|
||||||
|
pub children: Option<Vec<ObjectSubSprite>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A sprite that is drawn relative to an ObjectSprite.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ObjectSubSprite {
|
||||||
|
/// The sprite texture to draw
|
||||||
|
pub texture: content::TextureHandle,
|
||||||
|
|
||||||
|
/// This object's position, in world coordinates.
|
||||||
|
/// This is relative to this sprite's parent.
|
||||||
|
pub pos: Point3<f32>,
|
||||||
|
|
||||||
|
/// The size of this sprite,
|
||||||
|
/// given as height in world units.
|
||||||
|
pub size: f32,
|
||||||
|
|
||||||
|
/// This sprite's rotation
|
||||||
|
/// (relative to north, measured ccw)
|
||||||
|
/// Just as position, this is relative to this
|
||||||
|
/// subsprite's parent sprite.
|
||||||
|
pub angle: Deg<f32>,
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
|
use crate::content;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use galactica_content::{Content, TextureHandle};
|
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
|
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
|
||||||
use wgpu::BindGroupLayout;
|
use wgpu::BindGroupLayout;
|
||||||
|
|
||||||
pub(super) struct RawTexture {
|
pub(crate) struct RawTexture {
|
||||||
pub(super) view: wgpu::TextureView,
|
pub(crate) view: wgpu::TextureView,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawTexture {
|
impl RawTexture {
|
||||||
pub(super) fn from_bytes(
|
pub(crate) fn from_bytes(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
|
@ -19,7 +19,7 @@ impl RawTexture {
|
||||||
Self::from_image(device, queue, &img, Some(label))
|
Self::from_image(device, queue, &img, Some(label))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn from_image(
|
pub(crate) fn from_image(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
img: &image::DynamicImage,
|
img: &image::DynamicImage,
|
||||||
|
@ -76,8 +76,8 @@ pub struct Texture {
|
||||||
pub struct TextureArray {
|
pub struct TextureArray {
|
||||||
pub bind_group: wgpu::BindGroup,
|
pub bind_group: wgpu::BindGroup,
|
||||||
pub bind_group_layout: BindGroupLayout,
|
pub bind_group_layout: BindGroupLayout,
|
||||||
starfield_handle: TextureHandle,
|
starfield_handle: content::TextureHandle,
|
||||||
textures: HashMap<TextureHandle, Texture>,
|
textures: HashMap<content::TextureHandle, Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureArray {
|
impl TextureArray {
|
||||||
|
@ -85,14 +85,14 @@ impl TextureArray {
|
||||||
*self.textures.get(&self.starfield_handle).unwrap()
|
*self.textures.get(&self.starfield_handle).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_texture(&self, handle: TextureHandle) -> Texture {
|
pub fn get_texture(&self, handle: content::TextureHandle) -> Texture {
|
||||||
match self.textures.get(&handle) {
|
match self.textures.get(&handle) {
|
||||||
Some(x) => *x,
|
Some(x) => *x,
|
||||||
None => unreachable!("Tried to get a texture that doesn't exist"),
|
None => unreachable!("Tried to get a texture that doesn't exist"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &Content) -> Result<Self> {
|
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &content::Content) -> Result<Self> {
|
||||||
// Load all textures
|
// Load all textures
|
||||||
let mut texture_data = Vec::new();
|
let mut texture_data = Vec::new();
|
||||||
let mut textures = HashMap::new();
|
let mut textures = HashMap::new();
|
|
@ -36,9 +36,8 @@ impl BufferObject for TexturedVertex {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
pub struct StarfieldInstance {
|
pub struct StarfieldInstance {
|
||||||
/// Position in the starfield.
|
/// Position in the starfield. This is NOT world position!
|
||||||
///
|
///
|
||||||
/// This is NOT world position, i.e, different from sprite positioning!
|
|
||||||
/// The x and y coordinates here represent position relative to the center
|
/// The x and y coordinates here represent position relative to the center
|
||||||
/// of a starfield tile in world units, which is converted to world position
|
/// of a starfield tile in world units, which is converted to world position
|
||||||
/// by the starfield vertex shader.
|
/// by the starfield vertex shader.
|
|
@ -32,4 +32,4 @@ pub const STARFIELD_TEXTURE_NAME: &'static str = "starfield";
|
||||||
pub const CONTENT_ROOT: &'static str = "./content";
|
pub const CONTENT_ROOT: &'static str = "./content";
|
||||||
|
|
||||||
/// Root directory of game textures
|
/// Root directory of game textures
|
||||||
pub const TEXTURE_ROOT: &'static str = "./assets";
|
pub const TEXTURE_ROOT: &'static str = "./assets/render";
|
||||||
|
|
|
@ -1,18 +1,72 @@
|
||||||
use cgmath::Point2;
|
use cgmath::{Deg, InnerSpace, Point2};
|
||||||
use galactica_content::FactionHandle;
|
use galactica_render::{AnchoredUiPosition, ObjectSprite, UiSprite};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
use super::{camera::Camera, outfits, system::System};
|
use super::{camera::Camera, outfits, system::System};
|
||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts, content,
|
||||||
content::Content,
|
|
||||||
inputstatus::InputStatus,
|
inputstatus::InputStatus,
|
||||||
physics::{util, Physics, ShipHandle},
|
physics::{util, Physics, ShipHandle},
|
||||||
render::Sprite,
|
|
||||||
shipbehavior::{self, ShipBehavior},
|
shipbehavior::{self, ShipBehavior},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Ui {}
|
||||||
|
|
||||||
|
impl Ui {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_radar(
|
||||||
|
&self,
|
||||||
|
player: &ShipHandle,
|
||||||
|
physics: &Physics,
|
||||||
|
ct: &content::Content,
|
||||||
|
) -> Vec<UiSprite> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
let radar_range = 2000.0;
|
||||||
|
let radar_size = 300.0;
|
||||||
|
|
||||||
|
out.push(UiSprite {
|
||||||
|
texture: ct.get_texture_handle("ui::radar"),
|
||||||
|
pos: AnchoredUiPosition::NorthWest(Point2 { x: 10.0, y: -10.0 }),
|
||||||
|
dimensions: Point2 {
|
||||||
|
x: radar_size,
|
||||||
|
y: radar_size,
|
||||||
|
},
|
||||||
|
angle: Deg(0.0),
|
||||||
|
});
|
||||||
|
|
||||||
|
let (_, pr) = physics.get_ship_body(player).unwrap();
|
||||||
|
let pr = util::rigidbody_position(pr);
|
||||||
|
for (s, r) in physics.iter_ship_body() {
|
||||||
|
if s.physics_handle == *player {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let r = util::rigidbody_position(r);
|
||||||
|
let d = r - pr;
|
||||||
|
let m = d.magnitude() / radar_range;
|
||||||
|
if m < 0.8 {
|
||||||
|
out.push(UiSprite {
|
||||||
|
texture: ct.get_texture_handle("ui::blip"),
|
||||||
|
pos: AnchoredUiPosition::NorthWest(
|
||||||
|
Point2 {
|
||||||
|
x: radar_size / 2.0 + 10.0,
|
||||||
|
y: radar_size / -2.0 - 10.0,
|
||||||
|
} + (d / radar_range * 150.0),
|
||||||
|
),
|
||||||
|
dimensions: Point2 { x: 1.0, y: 1.0 } * 5.0f32.min((0.8 - m) * 50.0),
|
||||||
|
angle: Deg(0.0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
pub input: InputStatus,
|
pub input: InputStatus,
|
||||||
pub last_update: Instant,
|
pub last_update: Instant,
|
||||||
|
@ -22,13 +76,14 @@ pub struct Game {
|
||||||
paused: bool,
|
paused: bool,
|
||||||
pub time_scale: f32,
|
pub time_scale: f32,
|
||||||
|
|
||||||
|
ui: Ui,
|
||||||
physics: Physics,
|
physics: Physics,
|
||||||
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
|
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
|
||||||
content: Content,
|
content: content::Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new(ct: Content) -> Self {
|
pub fn new(ct: content::Content) -> Self {
|
||||||
let mut physics = Physics::new();
|
let mut physics = Physics::new();
|
||||||
|
|
||||||
let mut o1 = outfits::ShipOutfits::new(&ct.ships[0]);
|
let mut o1 = outfits::ShipOutfits::new(&ct.ships[0]);
|
||||||
|
@ -40,14 +95,14 @@ impl Game {
|
||||||
&ct.ships[0],
|
&ct.ships[0],
|
||||||
o1,
|
o1,
|
||||||
Point2 { x: 0.0, y: 0.0 },
|
Point2 { x: 0.0, y: 0.0 },
|
||||||
FactionHandle { index: 0 },
|
content::FactionHandle { index: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
let h2 = physics.add_ship(
|
let h2 = physics.add_ship(
|
||||||
&ct.ships[0],
|
&ct.ships[0],
|
||||||
outfits::ShipOutfits::new(&ct.ships[0]),
|
outfits::ShipOutfits::new(&ct.ships[0]),
|
||||||
Point2 { x: 300.0, y: 300.0 },
|
Point2 { x: 300.0, y: 300.0 },
|
||||||
FactionHandle { index: 0 },
|
content::FactionHandle { index: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut o2 = outfits::ShipOutfits::new(&ct.ships[0]);
|
let mut o2 = outfits::ShipOutfits::new(&ct.ships[0]);
|
||||||
|
@ -60,7 +115,7 @@ impl Game {
|
||||||
x: -300.0,
|
x: -300.0,
|
||||||
y: 300.0,
|
y: 300.0,
|
||||||
},
|
},
|
||||||
FactionHandle { index: 1 },
|
content::FactionHandle { index: 1 },
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
|
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
|
||||||
|
@ -84,6 +139,7 @@ impl Game {
|
||||||
physics,
|
physics,
|
||||||
shipbehaviors,
|
shipbehaviors,
|
||||||
content: ct,
|
content: ct,
|
||||||
|
ui: Ui::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +196,8 @@ impl Game {
|
||||||
self.last_update = Instant::now();
|
self.last_update = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sprites(&self) -> Vec<Sprite> {
|
pub fn get_object_sprites(&self) -> Vec<ObjectSprite> {
|
||||||
let mut sprites: Vec<Sprite> = Vec::new();
|
let mut sprites: Vec<ObjectSprite> = Vec::new();
|
||||||
|
|
||||||
sprites.append(&mut self.system.get_sprites());
|
sprites.append(&mut self.system.get_sprites());
|
||||||
sprites.extend(self.physics.get_ship_sprites());
|
sprites.extend(self.physics.get_ship_sprites());
|
||||||
|
@ -158,4 +214,10 @@ impl Game {
|
||||||
|
|
||||||
return sprites;
|
return sprites;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ui_sprites(&self) -> Vec<UiSprite> {
|
||||||
|
return self
|
||||||
|
.ui
|
||||||
|
.build_radar(&self.player, &self.physics, &self.content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use cgmath::{Deg, Point3};
|
use cgmath::{Deg, Point3};
|
||||||
use content::{OutfitSpace, TextureHandle};
|
use galactica_render::ObjectSubSprite;
|
||||||
|
|
||||||
use crate::{content, render::SubSprite};
|
use crate::content;
|
||||||
|
|
||||||
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
/// Represents a gun attached to a specific ship at a certain gunpoint.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -28,7 +28,7 @@ impl ShipGun {
|
||||||
pub struct OutfitStatSum {
|
pub struct OutfitStatSum {
|
||||||
pub engine_thrust: f32,
|
pub engine_thrust: f32,
|
||||||
pub steer_power: f32,
|
pub steer_power: f32,
|
||||||
pub engine_flare_textures: Vec<TextureHandle>,
|
pub engine_flare_textures: Vec<content::TextureHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutfitStatSum {
|
impl OutfitStatSum {
|
||||||
|
@ -67,9 +67,9 @@ impl OutfitStatSum {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ShipOutfits {
|
pub struct ShipOutfits {
|
||||||
pub stats: OutfitStatSum,
|
pub stats: OutfitStatSum,
|
||||||
pub total_space: OutfitSpace,
|
pub total_space: content::OutfitSpace,
|
||||||
|
|
||||||
available_space: OutfitSpace,
|
available_space: content::OutfitSpace,
|
||||||
outfits: Vec<content::Outfit>,
|
outfits: Vec<content::Outfit>,
|
||||||
guns: Vec<ShipGun>,
|
guns: Vec<ShipGun>,
|
||||||
enginepoints: Vec<content::EnginePoint>,
|
enginepoints: Vec<content::EnginePoint>,
|
||||||
|
@ -77,7 +77,7 @@ pub struct ShipOutfits {
|
||||||
|
|
||||||
// Minor performance optimization, since we
|
// Minor performance optimization, since we
|
||||||
// rarely need to re-compute these.
|
// rarely need to re-compute these.
|
||||||
engine_flare_sprites: Vec<SubSprite>,
|
engine_flare_sprites: Vec<ObjectSubSprite>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ShipOutfits {
|
impl<'a> ShipOutfits {
|
||||||
|
@ -177,7 +177,7 @@ impl<'a> ShipOutfits {
|
||||||
self.engine_flare_sprites = self
|
self.engine_flare_sprites = self
|
||||||
.enginepoints
|
.enginepoints
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| SubSprite {
|
.map(|p| ObjectSubSprite {
|
||||||
pos: Point3 {
|
pos: Point3 {
|
||||||
x: p.pos.x,
|
x: p.pos.x,
|
||||||
y: p.pos.y,
|
y: p.pos.y,
|
||||||
|
@ -190,7 +190,7 @@ impl<'a> ShipOutfits {
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_engine_flares(&self) -> Vec<SubSprite> {
|
pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> {
|
||||||
return self.engine_flare_sprites.clone();
|
return self.engine_flare_sprites.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
use cgmath::{Point3, Vector2};
|
use cgmath::{Point3, Vector2};
|
||||||
|
use galactica_render::{ObjectSprite, StarfieldStar};
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use super::SystemObject;
|
use super::SystemObject;
|
||||||
use crate::{consts, content, render::Sprite};
|
use crate::{consts, content};
|
||||||
|
|
||||||
pub struct StarfieldStar {
|
|
||||||
/// Star coordinates, in world space.
|
|
||||||
/// These are relative to the center of a starfield tile.
|
|
||||||
pub pos: Point3<f32>,
|
|
||||||
|
|
||||||
/// Height in game units.
|
|
||||||
/// Will be scaled for zoom, but not for distance.
|
|
||||||
pub size: f32,
|
|
||||||
|
|
||||||
/// Color/brightness variation. Random between 0 and 1.
|
|
||||||
/// Used in starfield shader.
|
|
||||||
pub tint: Vector2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct System {
|
pub struct System {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -59,7 +46,7 @@ impl System {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sprites(&self) -> Vec<Sprite> {
|
pub fn get_sprites(&self) -> Vec<ObjectSprite> {
|
||||||
return self.bodies.iter().map(|x| x.get_sprite()).collect();
|
return self.bodies.iter().map(|x| x.get_sprite()).collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
|
use crate::content;
|
||||||
use cgmath::{Deg, Point3};
|
use cgmath::{Deg, Point3};
|
||||||
use galactica_content::TextureHandle;
|
use galactica_render::ObjectSprite;
|
||||||
|
|
||||||
use crate::render::Sprite;
|
|
||||||
|
|
||||||
pub struct SystemObject {
|
pub struct SystemObject {
|
||||||
pub sprite_texture: TextureHandle,
|
pub sprite_texture: content::TextureHandle,
|
||||||
pub pos: Point3<f32>,
|
pub pos: Point3<f32>,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub angle: Deg<f32>,
|
pub angle: Deg<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemObject {
|
impl SystemObject {
|
||||||
pub(super) fn get_sprite(&self) -> Sprite {
|
pub(crate) fn get_sprite(&self) -> ObjectSprite {
|
||||||
return Sprite {
|
return ObjectSprite {
|
||||||
texture: self.sprite_texture,
|
texture: self.sprite_texture,
|
||||||
pos: self.pos,
|
pos: self.pos,
|
||||||
angle: self.angle,
|
angle: self.angle,
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -3,13 +3,12 @@ mod game;
|
||||||
mod inputstatus;
|
mod inputstatus;
|
||||||
mod objects;
|
mod objects;
|
||||||
mod physics;
|
mod physics;
|
||||||
mod render;
|
|
||||||
mod shipbehavior;
|
mod shipbehavior;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
pub use galactica_content as content;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use galactica_content as content;
|
use std::path::PathBuf;
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{Event, KeyboardInput, WindowEvent},
|
event::{Event, KeyboardInput, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
@ -26,17 +25,22 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
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(render::GPUState::new(window, &content))?;
|
let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?;
|
||||||
|
|
||||||
let mut game = game::Game::new(content);
|
let mut game = game::Game::new(content);
|
||||||
gpu.update_starfield_buffer(&game);
|
gpu.update_starfield_buffer(&game.system.starfield);
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
match event {
|
match event {
|
||||||
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
|
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
|
||||||
match gpu.render(&game) {
|
match gpu.render(
|
||||||
|
game.camera.pos,
|
||||||
|
game.camera.zoom,
|
||||||
|
&game.get_object_sprites(),
|
||||||
|
&game.get_ui_sprites(),
|
||||||
|
) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(wgpu::SurfaceError::Lost) => gpu.resize(&game, gpu.window_size),
|
Err(wgpu::SurfaceError::Lost) => gpu.resize(&game.system.starfield),
|
||||||
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),
|
||||||
|
@ -71,11 +75,11 @@ fn main() -> Result<()> {
|
||||||
WindowEvent::MouseWheel { delta, phase, .. } => {
|
WindowEvent::MouseWheel { delta, phase, .. } => {
|
||||||
game.process_scroll(delta, phase);
|
game.process_scroll(delta, phase);
|
||||||
}
|
}
|
||||||
WindowEvent::Resized(physical_size) => {
|
WindowEvent::Resized(_) => {
|
||||||
gpu.resize(&game, *physical_size);
|
gpu.resize(&game.system.starfield);
|
||||||
}
|
}
|
||||||
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
WindowEvent::ScaleFactorChanged { .. } => {
|
||||||
gpu.resize(&game, **new_inner_size);
|
gpu.resize(&game.system.starfield);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
use cgmath::{Deg, InnerSpace, Point3, Vector2};
|
use cgmath::{Deg, InnerSpace, Point3, Vector2};
|
||||||
use galactica_content::{FactionHandle, TextureHandle};
|
use galactica_render::ObjectSprite;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
||||||
geometry::{ColliderBuilder, ColliderHandle},
|
geometry::{ColliderBuilder, ColliderHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{physics::util, render::Sprite};
|
use crate::{content, physics::util};
|
||||||
|
|
||||||
pub struct ProjectileBuilder {
|
pub struct ProjectileBuilder {
|
||||||
pub rigid_body: RigidBodyBuilder,
|
pub rigid_body: RigidBodyBuilder,
|
||||||
pub collider: ColliderBuilder,
|
pub collider: ColliderBuilder,
|
||||||
pub sprite_texture: TextureHandle,
|
pub sprite_texture: content::TextureHandle,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
pub faction: FactionHandle,
|
pub faction: content::FactionHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectileBuilder {
|
impl ProjectileBuilder {
|
||||||
|
@ -35,11 +35,11 @@ impl ProjectileBuilder {
|
||||||
pub struct Projectile {
|
pub struct Projectile {
|
||||||
pub rigid_body: RigidBodyHandle,
|
pub rigid_body: RigidBodyHandle,
|
||||||
pub collider: ColliderHandle,
|
pub collider: ColliderHandle,
|
||||||
pub sprite_texture: TextureHandle,
|
pub sprite_texture: content::TextureHandle,
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
pub damage: f32,
|
pub damage: f32,
|
||||||
pub faction: FactionHandle,
|
pub faction: content::FactionHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Projectile {
|
impl Projectile {
|
||||||
|
@ -51,14 +51,14 @@ impl Projectile {
|
||||||
return self.lifetime < 0.0;
|
return self.lifetime < 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
|
pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
|
||||||
let pos = util::rigidbody_position(r);
|
let pos = util::rigidbody_position(r);
|
||||||
let rot = util::rigidbody_rotation(r);
|
let rot = util::rigidbody_rotation(r);
|
||||||
|
|
||||||
// Sprites point north at 0 degrees
|
// Sprites point north at 0 degrees
|
||||||
let ang: Deg<f32> = rot.angle(Vector2 { x: 1.0, y: 0.0 }).into();
|
let ang: Deg<f32> = rot.angle(Vector2 { x: 1.0, y: 0.0 }).into();
|
||||||
|
|
||||||
Sprite {
|
ObjectSprite {
|
||||||
texture: self.sprite_texture,
|
texture: self.sprite_texture,
|
||||||
pos: Point3 {
|
pos: Point3 {
|
||||||
x: pos.x,
|
x: pos.x,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2};
|
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2};
|
||||||
use content::{FactionHandle, TextureHandle};
|
use content::{FactionHandle, TextureHandle};
|
||||||
|
use galactica_render::ObjectSprite;
|
||||||
use nalgebra::vector;
|
use nalgebra::vector;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
|
@ -13,7 +14,6 @@ use crate::{
|
||||||
content,
|
content,
|
||||||
game::outfits,
|
game::outfits,
|
||||||
physics::{util, ShipHandle},
|
physics::{util, ShipHandle},
|
||||||
render::Sprite,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ShipTickResult {
|
pub struct ShipTickResult {
|
||||||
|
@ -157,14 +157,14 @@ impl Ship {
|
||||||
return ShipTickResult { projectiles: p };
|
return ShipTickResult { projectiles: p };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
|
pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
|
||||||
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);
|
||||||
|
|
||||||
// Sprites point north at 0 degrees
|
// Sprites point north at 0 degrees
|
||||||
let ship_ang: Deg<f32> = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }).into();
|
let ship_ang: Deg<f32> = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }).into();
|
||||||
|
|
||||||
Sprite {
|
ObjectSprite {
|
||||||
pos: (ship_pos.x, ship_pos.y, 1.0).into(),
|
pos: (ship_pos.x, ship_pos.y, 1.0).into(),
|
||||||
texture: self.sprite_texture,
|
texture: self.sprite_texture,
|
||||||
angle: -ship_ang,
|
angle: -ship_ang,
|
||||||
|
|
|
@ -6,5 +6,5 @@ pub use physics::Physics;
|
||||||
|
|
||||||
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
|
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct ShipHandle(pub(super) RigidBodyHandle, ColliderHandle);
|
pub struct ShipHandle(pub(super) RigidBodyHandle, ColliderHandle);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use cgmath::Point2;
|
use cgmath::Point2;
|
||||||
use content::{Content, FactionHandle};
|
|
||||||
use crossbeam::channel::Receiver;
|
use crossbeam::channel::Receiver;
|
||||||
|
use galactica_render::ObjectSprite;
|
||||||
use nalgebra::vector;
|
use nalgebra::vector;
|
||||||
use rapier2d::{
|
use rapier2d::{
|
||||||
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
|
||||||
|
@ -10,7 +10,7 @@ use rapier2d::{
|
||||||
use std::{collections::HashMap, f32::consts::PI};
|
use std::{collections::HashMap, f32::consts::PI};
|
||||||
|
|
||||||
use super::{wrapper::Wrapper, ShipHandle};
|
use super::{wrapper::Wrapper, ShipHandle};
|
||||||
use crate::{content, game::outfits, objects, render::Sprite};
|
use crate::{content, game::outfits, objects};
|
||||||
|
|
||||||
/// Keeps track of all objects in the world that we can interact with.
|
/// Keeps track of all objects in the world that we can interact with.
|
||||||
/// Also wraps our physics engine
|
/// Also wraps our physics engine
|
||||||
|
@ -84,7 +84,7 @@ impl Physics {
|
||||||
ct: &content::Ship,
|
ct: &content::Ship,
|
||||||
outfits: outfits::ShipOutfits,
|
outfits: outfits::ShipOutfits,
|
||||||
position: Point2<f32>,
|
position: Point2<f32>,
|
||||||
faction: FactionHandle,
|
faction: content::FactionHandle,
|
||||||
) -> ShipHandle {
|
) -> ShipHandle {
|
||||||
let cl = ColliderBuilder::convex_decomposition(
|
let cl = ColliderBuilder::convex_decomposition(
|
||||||
&ct.collision.points[..],
|
&ct.collision.points[..],
|
||||||
|
@ -114,7 +114,7 @@ impl Physics {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, t: f32, ct: &Content) {
|
pub fn step(&mut self, t: f32, ct: &content::Content) {
|
||||||
// Run ship updates
|
// Run ship updates
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut to_remove = Vec::new();
|
let mut to_remove = Vec::new();
|
||||||
|
@ -205,13 +205,13 @@ impl Physics {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
|
pub fn get_ship_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ {
|
||||||
self.ships
|
self.ships
|
||||||
.values()
|
.values()
|
||||||
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.physics_handle.0]))
|
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.physics_handle.0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_projectile_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
|
pub fn get_projectile_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ {
|
||||||
self.projectiles
|
self.projectiles
|
||||||
.values()
|
.values()
|
||||||
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.rigid_body]))
|
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.rigid_body]))
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
mod consts;
|
|
||||||
mod globaldata;
|
|
||||||
mod gpustate;
|
|
||||||
mod pipeline;
|
|
||||||
mod sprite;
|
|
||||||
mod texturearray;
|
|
||||||
mod vertexbuffer;
|
|
||||||
|
|
||||||
pub use gpustate::GPUState;
|
|
||||||
pub use sprite::{Sprite, SubSprite};
|
|
|
@ -1,42 +0,0 @@
|
||||||
use cgmath::{Deg, Point3};
|
|
||||||
use galactica_content::TextureHandle;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Sprite {
|
|
||||||
/// The sprite texture to draw
|
|
||||||
pub texture: TextureHandle,
|
|
||||||
|
|
||||||
/// This object's position, in world coordinates.
|
|
||||||
pub pos: Point3<f32>,
|
|
||||||
|
|
||||||
/// The size of this sprite,
|
|
||||||
/// given as height in world units.
|
|
||||||
pub size: f32,
|
|
||||||
|
|
||||||
/// This sprite's rotation
|
|
||||||
/// (relative to north, measured ccw)
|
|
||||||
pub angle: Deg<f32>,
|
|
||||||
|
|
||||||
/// Sprites that should be drawn relative to this sprite.
|
|
||||||
pub children: Option<Vec<SubSprite>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SubSprite {
|
|
||||||
/// The sprite texture to draw
|
|
||||||
pub texture: TextureHandle,
|
|
||||||
|
|
||||||
/// This object's position, in world coordinates.
|
|
||||||
/// This is relative to this sprite's parent.
|
|
||||||
pub pos: Point3<f32>,
|
|
||||||
|
|
||||||
/// The size of this sprite,
|
|
||||||
/// given as height in world units.
|
|
||||||
pub size: f32,
|
|
||||||
|
|
||||||
/// This sprite's rotation
|
|
||||||
/// (relative to north, measured ccw)
|
|
||||||
/// Just as position, this is relative to this
|
|
||||||
/// subsprite's parent sprite.
|
|
||||||
pub angle: Deg<f32>,
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use cgmath::{Deg, InnerSpace};
|
use cgmath::{Deg, InnerSpace};
|
||||||
use galactica_content as content;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
content,
|
||||||
inputstatus::InputStatus,
|
inputstatus::InputStatus,
|
||||||
physics::{util, Physics, ShipHandle},
|
physics::{util, Physics, ShipHandle},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue