Compare commits
No commits in common. "7334ebd00e8c095dccdb819db2ee94fffaadf66e" and "246079b33a3b48b92f89f36cf6274dc65931bf8c" have entirely different histories.
7334ebd00e
...
246079b33a
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -777,9 +777,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glyphon"
|
name = "glyphon"
|
||||||
version = "0.4.1"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/grovesNL/glyphon.git?rev=b15437b87f4082e7a96a2ba05ed5063a6047cf95#b15437b87f4082e7a96a2ba05ed5063a6047cf95"
|
||||||
checksum = "17e87235dcba36007d4990db6253ac04e4efbd16df1f0812c2fefb1217aa2644"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
"etagere",
|
"etagere",
|
||||||
@ -1044,9 +1043,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.12.1"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7"
|
checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
@ -63,4 +63,6 @@ anyhow = "1.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 = "0.4.1"
|
|
||||||
|
# Glyphon's crates.io release doesn't support wgpu 0.18 yet
|
||||||
|
glyphon = { git = "https://github.com/grovesNL/glyphon.git", rev = "b15437b87f4082e7a96a2ba05ed5063a6047cf95" }
|
||||||
|
2
TODO.md
2
TODO.md
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
|
|
||||||
## Small jobs
|
## Small jobs
|
||||||
- 🌟 Better planet desc formatting
|
|
||||||
- Procedural suns
|
- Procedural suns
|
||||||
- 🌟 Back arrow -> reverse
|
- 🌟 Back arrow -> reverse
|
||||||
- No wobble for ai ships & autopilot
|
- No wobble for ai ships & autopilot
|
||||||
@ -44,7 +43,6 @@
|
|||||||
- Projectile performance
|
- Projectile performance
|
||||||
- Starfield clusters, shader instead of an array?
|
- Starfield clusters, shader instead of an array?
|
||||||
- Collider groups for factions? (projectile optimization)
|
- Collider groups for factions? (projectile optimization)
|
||||||
- Better error when sprite is missing from atlas
|
|
||||||
|
|
||||||
## 🌟 Player selection
|
## 🌟 Player selection
|
||||||
- Planet name, ring, and distance
|
- Planet name, ring, and distance
|
||||||
|
2
assets
2
assets
@ -1 +1 @@
|
|||||||
Subproject commit b4fe8111c7b1cb17987bbff095bd50e61e2d8998
|
Subproject commit 9746acb16c6e2c5f6f232e8d1b53e27fb9ef486e
|
@ -61,16 +61,6 @@ file = "ui/radarframe.png"
|
|||||||
[sprite."ui::centerarrow"]
|
[sprite."ui::centerarrow"]
|
||||||
file = "ui/center-arrow.png"
|
file = "ui/center-arrow.png"
|
||||||
|
|
||||||
[sprite."ui::planet"]
|
|
||||||
file = "ui/planet.png"
|
|
||||||
|
|
||||||
[sprite."ui::landscape::test"]
|
|
||||||
file = "ui/landscape/test.png"
|
|
||||||
|
|
||||||
[sprite."ui::landscapemask"]
|
|
||||||
file = "ui/landscape-mask.png"
|
|
||||||
|
|
||||||
|
|
||||||
[sprite."particle::blaster"]
|
[sprite."particle::blaster"]
|
||||||
timing.duration = 0.15
|
timing.duration = 0.15
|
||||||
repeat = "once"
|
repeat = "once"
|
||||||
|
@ -15,20 +15,6 @@ object.earth.size = 1000
|
|||||||
|
|
||||||
# TODO: satisfy conditions to land
|
# TODO: satisfy conditions to land
|
||||||
object.earth.landable = true
|
object.earth.landable = true
|
||||||
object.earth.name = "Earth"
|
|
||||||
object.earth.desc = """
|
|
||||||
The ancestral home world of humanity, Earth has a population twice that of any other inhabited planet.
|
|
||||||
Sprawling cities cover large portions of its surface, many of them overcrowded and dangerous.
|
|
||||||
Some people work to scrape together enough money to leave, while at the same time others, born
|
|
||||||
on distant worlds, make a pilgrimage of sorts to see this planet that once cradled the entirety
|
|
||||||
of the human species.
|
|
||||||
<br><br>
|
|
||||||
Earth is also the capital of the Republic. Representative government becomes complicated when
|
|
||||||
one planet has a greater population than a hundred planets elsewhere. As a result,
|
|
||||||
settlements of less than a million are grouped together into planetary districts that
|
|
||||||
elect a single representative between them - a source of much frustration in the frontier worlds.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
object.luna.sprite = "planet::luna"
|
object.luna.sprite = "planet::luna"
|
||||||
object.luna.position.center = "earth"
|
object.luna.position.center = "earth"
|
||||||
|
@ -29,8 +29,6 @@ pub(crate) mod syntax {
|
|||||||
pub radius: Option<f32>,
|
pub radius: Option<f32>,
|
||||||
pub angle: Option<f32>,
|
pub angle: Option<f32>,
|
||||||
pub landable: Option<bool>,
|
pub landable: Option<bool>,
|
||||||
pub name: Option<String>,
|
|
||||||
pub desc: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@ -124,12 +122,6 @@ pub struct SystemObject {
|
|||||||
|
|
||||||
/// If true, ships may land on this object
|
/// If true, ships may land on this object
|
||||||
pub landable: bool,
|
pub landable: bool,
|
||||||
|
|
||||||
/// The display name of this object
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
/// The description of this object
|
|
||||||
pub desc: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function for resolve_position, never called on its own.
|
/// Helper function for resolve_position, never called on its own.
|
||||||
@ -231,19 +223,6 @@ impl crate::Build for System {
|
|||||||
body_index: 0,
|
body_index: 0,
|
||||||
},
|
},
|
||||||
landable: obj.landable.unwrap_or(false),
|
landable: obj.landable.unwrap_or(false),
|
||||||
name: obj
|
|
||||||
.name
|
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.clone())
|
|
||||||
.unwrap_or("".to_string()),
|
|
||||||
|
|
||||||
// TODO: better linebreaks, handle double spaces
|
|
||||||
// Tabs
|
|
||||||
desc: obj
|
|
||||||
.desc
|
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.replace("\n", " ").replace("<br>", "\n"))
|
|
||||||
.unwrap_or("".to_string()),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ impl<'a> Game {
|
|||||||
ShipHandle { index: 0 },
|
ShipHandle { index: 0 },
|
||||||
FactionHandle { index: 0 },
|
FactionHandle { index: 0 },
|
||||||
ShipPersonality::Player,
|
ShipPersonality::Player,
|
||||||
Point2::new(0.0, 4000.0),
|
Point2::new(0.0, 0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let s = self.state.systemsim.get_ship_mut(&player).unwrap();
|
let s = self.state.systemsim.get_ship_mut(&player).unwrap();
|
||||||
|
@ -28,12 +28,6 @@ fn anchor(
|
|||||||
} else if anchor == 5u { // NE NE
|
} else if anchor == 5u { // NE NE
|
||||||
trans += vec2(window_dim.x, window_dim.y) / 2.0;
|
trans += vec2(window_dim.x, window_dim.y) / 2.0;
|
||||||
trans += vec2(-dim.x, -dim.y) / 2.0;
|
trans += vec2(-dim.x, -dim.y) / 2.0;
|
||||||
} else if anchor == 6u { // C C
|
|
||||||
trans += vec2(0.0, 0.0) / 2.0;
|
|
||||||
trans += vec2(0.0, 0.0) / 2.0;
|
|
||||||
} else if anchor == 7u { // C NW
|
|
||||||
trans += vec2(0.0, 0.0) / 2.0;
|
|
||||||
trans += vec2(dim.x, -dim.y) / 2.0;
|
|
||||||
} else { // center / center as default, since it's the most visible variant.
|
} else { // center / center as default, since it's the most visible variant.
|
||||||
trans += vec2(0.0, 0.0) / 2.0;
|
trans += vec2(0.0, 0.0) / 2.0;
|
||||||
trans += vec2(0.0, 0.0) / 2.0;
|
trans += vec2(0.0, 0.0) / 2.0;
|
||||||
|
@ -7,7 +7,6 @@ struct InstanceInput {
|
|||||||
@location(5) size: f32,
|
@location(5) size: f32,
|
||||||
@location(6) color_transform: vec4<f32>,
|
@location(6) color_transform: vec4<f32>,
|
||||||
@location(7) sprite_index: u32,
|
@location(7) sprite_index: u32,
|
||||||
@location(8) mask_index: vec2<u32>,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@ -19,9 +18,7 @@ struct VertexOutput {
|
|||||||
@builtin(position) position: vec4<f32>,
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) texture_coords: vec2<f32>,
|
@location(0) texture_coords: vec2<f32>,
|
||||||
@location(1) texture_index: u32,
|
@location(1) texture_index: u32,
|
||||||
@location(2) mask_coords: vec2<f32>,
|
@location(2) color_transform: vec4<f32>,
|
||||||
@location(3) mask_index: vec2<u32>,
|
|
||||||
@location(4) color_transform: vec4<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
@ -75,7 +72,7 @@ fn vertex_main(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: function to get texture from sprite
|
// TODO: animate
|
||||||
// Pick texture frame
|
// Pick texture frame
|
||||||
let t = global_atlas[u32(animate(instance.sprite_index, global_data.current_time.x, 0.0))];
|
let t = global_atlas[u32(animate(instance.sprite_index, global_data.current_time.x, 0.0))];
|
||||||
out.texture_index = u32(t.atlas_texture);
|
out.texture_index = u32(t.atlas_texture);
|
||||||
@ -87,55 +84,16 @@ fn vertex_main(
|
|||||||
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick mask image if mask is enabled
|
|
||||||
// x coordinate of mask index is either 0 or 1, telling us whether or not to use a mask.
|
|
||||||
// y coordinate is mask sprite index
|
|
||||||
if instance.mask_index.x == 1u {
|
|
||||||
let ms = global_sprites[instance.mask_index.y].first_frame;
|
|
||||||
let m = global_atlas[ms];
|
|
||||||
out.mask_index = vec2(1u, u32(m.atlas_texture));
|
|
||||||
out.mask_coords = vec2(m.xpos, m.ypos);
|
|
||||||
if vertex.texture_coords.x == 1.0 {
|
|
||||||
out.mask_coords = vec2(out.mask_coords.x + m.width, out.mask_coords.y);
|
|
||||||
}
|
|
||||||
if vertex.texture_coords.y == 1.0 {
|
|
||||||
out.mask_coords = vec2(out.mask_coords.x, out.mask_coords.y + m.height);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.mask_coords = vec2(0.0, 0.0);
|
|
||||||
out.mask_index = vec2(0u, 0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return textureSampleLevel(
|
||||||
var mask: f32 = 1.0;
|
|
||||||
if in.mask_index.x == 1u {
|
|
||||||
mask = textureSampleLevel(
|
|
||||||
texture_array[in.mask_index.y],
|
|
||||||
sampler_array[0],
|
|
||||||
in.mask_coords,
|
|
||||||
0.0
|
|
||||||
).a;
|
|
||||||
}
|
|
||||||
|
|
||||||
var color: vec4<f32> = textureSampleLevel(
|
|
||||||
texture_array[in.texture_index],
|
texture_array[in.texture_index],
|
||||||
sampler_array[0],
|
sampler_array[0],
|
||||||
in.texture_coords,
|
in.texture_coords,
|
||||||
0.0
|
0.0
|
||||||
).rgba * in.color_transform;
|
).rgba * in.color_transform;
|
||||||
|
|
||||||
// Apply mask and discard fully transparent pixels
|
|
||||||
color = vec4(color.rgb, color.a *mask);
|
|
||||||
if color.a == 0.0 {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
}
|
@ -28,14 +28,6 @@ pub enum PositionAnchor {
|
|||||||
/// Position of this sprite's ne corner,
|
/// Position of this sprite's ne corner,
|
||||||
/// relative to the ne corner of the window.
|
/// relative to the ne corner of the window.
|
||||||
NeNe,
|
NeNe,
|
||||||
|
|
||||||
/// Position of this sprite's center,
|
|
||||||
/// relative to the center of the window.
|
|
||||||
CC,
|
|
||||||
|
|
||||||
/// Position of this sprite's NW corner,
|
|
||||||
/// relative to the center of the window.
|
|
||||||
CNw,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// These offsets are implemented in wgsl shaders.
|
// These offsets are implemented in wgsl shaders.
|
||||||
@ -50,8 +42,6 @@ impl PositionAnchor {
|
|||||||
Self::NwSw => 3,
|
Self::NwSw => 3,
|
||||||
Self::NwSe => 4,
|
Self::NwSe => 4,
|
||||||
Self::NeNe => 5,
|
Self::NeNe => 5,
|
||||||
Self::CC => 6,
|
|
||||||
Self::CNw => 7,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
78
crates/render/src/datastructs.rs
Normal file
78
crates/render/src/datastructs.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use galactica_content::{Content, SystemHandle};
|
||||||
|
use galactica_playeragent::PlayerAgent;
|
||||||
|
use galactica_system::phys::{ParticleBuilder, PhysSim};
|
||||||
|
use galactica_util::timing::Timing;
|
||||||
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
|
use nalgebra::Vector2;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wgpu::BufferAddress;
|
||||||
|
use winit::window::Window;
|
||||||
|
|
||||||
|
use crate::{globaluniform::GlobalUniform, vertexbuffer::VertexBuffer};
|
||||||
|
|
||||||
|
/// Bundles parameters passed to a single call to GPUState::render
|
||||||
|
pub struct RenderInput<'a> {
|
||||||
|
/// Camera position, in world units
|
||||||
|
pub camera_pos: Vector2<f32>,
|
||||||
|
|
||||||
|
/// Player ship data
|
||||||
|
pub player: &'a PlayerAgent,
|
||||||
|
|
||||||
|
/// The system we're currently in
|
||||||
|
pub current_system: SystemHandle,
|
||||||
|
|
||||||
|
/// Height of screen, in world units
|
||||||
|
pub camera_zoom: f32,
|
||||||
|
|
||||||
|
/// The world state to render
|
||||||
|
pub systemsim: &'a PhysSim,
|
||||||
|
|
||||||
|
// TODO: handle overflow. is it a problem?
|
||||||
|
/// The current time, in seconds
|
||||||
|
pub current_time: f32,
|
||||||
|
|
||||||
|
/// Game content
|
||||||
|
pub ct: &'a Content,
|
||||||
|
|
||||||
|
/// Particles to spawn during this frame
|
||||||
|
pub particles: &'a Vec<ParticleBuilder>,
|
||||||
|
|
||||||
|
/// Time we spent in each part of the game loop
|
||||||
|
pub timing: Timing,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renderer state. A reference to this struct is often passed to helper functions.
|
||||||
|
pub(crate) struct RenderState {
|
||||||
|
pub window: Window,
|
||||||
|
pub window_size: winit::dpi::PhysicalSize<u32>,
|
||||||
|
pub window_aspect: f32,
|
||||||
|
|
||||||
|
pub queue: wgpu::Queue,
|
||||||
|
pub global_uniform: GlobalUniform,
|
||||||
|
pub vertex_buffers: VertexBuffers,
|
||||||
|
|
||||||
|
pub text_font_system: FontSystem,
|
||||||
|
pub text_cache: SwashCache,
|
||||||
|
pub text_atlas: TextAtlas,
|
||||||
|
pub text_renderer: TextRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vertex buffers
|
||||||
|
pub(crate) struct VertexBuffers {
|
||||||
|
// Keeps track of length of each buffer
|
||||||
|
// Most of these are reset on each frame.
|
||||||
|
//
|
||||||
|
// The exception is particle_counter, which
|
||||||
|
// is never reset, and loops back to zero once
|
||||||
|
// it exceeds buffer length
|
||||||
|
pub object_counter: BufferAddress,
|
||||||
|
pub ui_counter: BufferAddress,
|
||||||
|
pub particle_counter: BufferAddress,
|
||||||
|
pub radialbar_counter: BufferAddress,
|
||||||
|
|
||||||
|
pub object: Rc<VertexBuffer>,
|
||||||
|
pub starfield: Rc<VertexBuffer>,
|
||||||
|
pub ui: Rc<VertexBuffer>,
|
||||||
|
pub particle: Rc<VertexBuffer>,
|
||||||
|
pub radialbar: Rc<VertexBuffer>,
|
||||||
|
}
|
@ -3,7 +3,9 @@ use galactica_content::Content;
|
|||||||
use wgpu;
|
use wgpu;
|
||||||
use winit;
|
use winit;
|
||||||
|
|
||||||
use crate::{starfield::Starfield, texturearray::TextureArray, ui::UiManager, RenderState};
|
use crate::{
|
||||||
|
datastructs::RenderState, starfield::Starfield, texturearray::TextureArray, ui::UiManager,
|
||||||
|
};
|
||||||
|
|
||||||
/// GPUState is very big, so its methods have been split
|
/// GPUState is very big, so its methods have been split
|
||||||
/// among the following files.
|
/// among the following files.
|
||||||
|
@ -1,11 +1,28 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
|
use galactica_util::constants::{
|
||||||
|
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT,
|
||||||
|
};
|
||||||
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::GlobalUniform, pipeline::PipelineBuilder, shaderprocessor::preprocess_shader,
|
datastructs::{RenderState, VertexBuffers},
|
||||||
starfield::Starfield, texturearray::TextureArray, ui::UiManager, GPUState, RenderState,
|
globaluniform::GlobalUniform,
|
||||||
VertexBuffers,
|
pipeline::PipelineBuilder,
|
||||||
|
shaderprocessor::preprocess_shader,
|
||||||
|
starfield::Starfield,
|
||||||
|
texturearray::TextureArray,
|
||||||
|
ui::UiManager,
|
||||||
|
vertexbuffer::{
|
||||||
|
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
||||||
|
types::{
|
||||||
|
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
|
||||||
|
UiInstance,
|
||||||
|
},
|
||||||
|
VertexBuffer,
|
||||||
|
},
|
||||||
|
GPUState,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
@ -73,7 +90,52 @@ impl GPUState {
|
|||||||
surface.configure(&device, &config);
|
surface.configure(&device, &config);
|
||||||
}
|
}
|
||||||
|
|
||||||
let vertex_buffers = VertexBuffers::new(&device, ct);
|
let vertex_buffers = VertexBuffers {
|
||||||
|
object_counter: 0,
|
||||||
|
ui_counter: 0,
|
||||||
|
particle_counter: 0,
|
||||||
|
radialbar_counter: 0,
|
||||||
|
|
||||||
|
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
|
||||||
|
"object",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
OBJECT_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
|
||||||
|
"starfield",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
ct.get_config().starfield_instance_limit,
|
||||||
|
)),
|
||||||
|
|
||||||
|
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
||||||
|
"ui",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
UI_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
|
||||||
|
"particle",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
PARTICLE_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
|
||||||
|
"radial bar",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
10,
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
// Load uniforms
|
// Load uniforms
|
||||||
let global_uniform = GlobalUniform::new(&device);
|
let global_uniform = GlobalUniform::new(&device);
|
||||||
@ -135,7 +197,7 @@ impl GPUState {
|
|||||||
))
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(vertex_buffers.get_object())
|
.set_vertex_buffer(&vertex_buffers.object)
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -151,7 +213,7 @@ impl GPUState {
|
|||||||
))
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(vertex_buffers.get_starfield())
|
.set_vertex_buffer(&vertex_buffers.starfield)
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -163,7 +225,7 @@ impl GPUState {
|
|||||||
))
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(vertex_buffers.get_ui())
|
.set_vertex_buffer(&vertex_buffers.ui)
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -179,7 +241,7 @@ impl GPUState {
|
|||||||
))
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(vertex_buffers.get_particle())
|
.set_vertex_buffer(&vertex_buffers.particle)
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -195,7 +257,7 @@ impl GPUState {
|
|||||||
))
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(vertex_buffers.get_radialbar())
|
.set_vertex_buffer(&vertex_buffers.radialbar)
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -219,7 +281,7 @@ impl GPUState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
ui: UiManager::new(ct, &mut state),
|
ui: UiManager::new(&mut state),
|
||||||
device,
|
device,
|
||||||
config,
|
config,
|
||||||
surface,
|
surface,
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use galactica_system::data::ShipState;
|
use galactica_system::data::ShipState;
|
||||||
use galactica_util::to_radians;
|
use galactica_util::{constants::OBJECT_SPRITE_INSTANCE_LIMIT, to_radians};
|
||||||
use nalgebra::{Point2, Point3};
|
use nalgebra::{Point2, Point3};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderInput,
|
globaluniform::ObjectData,
|
||||||
|
vertexbuffer::{types::ObjectInstance, BufferObject},
|
||||||
|
GPUState, RenderInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
@ -66,7 +68,7 @@ impl GPUState {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = self.state.vertex_buffers.object_counter;
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
@ -83,11 +85,22 @@ impl GPUState {
|
|||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enforce buffer limit
|
||||||
|
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Sprite limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.vertex_buffers.object.instances,
|
||||||
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: ship_cnt.sprite.get_index(),
|
sprite_index: ship_cnt.sprite.get_index(),
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
|
|
||||||
let flare = ship.data.get_outfits().get_flare_sprite(state.ct);
|
let flare = ship.data.get_outfits().get_flare_sprite(state.ct);
|
||||||
if {
|
if {
|
||||||
@ -102,7 +115,7 @@ impl GPUState {
|
|||||||
for engine_point in &ship_cnt.engines {
|
for engine_point in &ship_cnt.engines {
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * self.state.get_object_counter() as u64,
|
ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
// Note that we adjust the y-coordinate for half-height,
|
// Note that we adjust the y-coordinate for half-height,
|
||||||
// not the x-coordinate, even though our ships point east
|
// not the x-coordinate, even though our ships point east
|
||||||
@ -122,10 +135,23 @@ impl GPUState {
|
|||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
// Enforce buffer limit
|
||||||
|
if self.state.vertex_buffers.object_counter as u64
|
||||||
|
> OBJECT_SPRITE_INSTANCE_LIMIT
|
||||||
|
{
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Sprite limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.vertex_buffers.object.instances,
|
||||||
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: flare.unwrap().get_index(),
|
sprite_index: flare.unwrap().get_index(),
|
||||||
object_index: self.state.get_object_counter() as u32,
|
object_index: self.state.vertex_buffers.object_counter as u32,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,7 +191,7 @@ impl GPUState {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = self.state.vertex_buffers.object_counter;
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
@ -182,11 +208,22 @@ impl GPUState {
|
|||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enforce buffer limit
|
||||||
|
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Sprite limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.vertex_buffers.object.instances,
|
||||||
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: proj_cnt.sprite.get_index(),
|
sprite_index: proj_cnt.sprite.get_index(),
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +255,7 @@ impl GPUState {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = self.state.vertex_buffers.object_counter;
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
@ -235,11 +272,22 @@ impl GPUState {
|
|||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Enforce buffer limit
|
||||||
|
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Sprite limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.vertex_buffers.object.instances,
|
||||||
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: o.sprite.get_index(),
|
sprite_index: o.sprite.get_index(),
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use wgpu;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::GlobalDataContent,
|
globaluniform::GlobalDataContent,
|
||||||
vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance},
|
vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance, BufferObject},
|
||||||
RenderInput,
|
RenderInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,7 +45,11 @@ impl super::GPUState {
|
|||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.state.frame_reset();
|
self.state.vertex_buffers.object_counter = 0;
|
||||||
|
self.state.vertex_buffers.ui_counter = 0;
|
||||||
|
self.state.vertex_buffers.radialbar_counter = 0;
|
||||||
|
// Don't reset particle counter, it's special
|
||||||
|
|
||||||
let s = input.ct.get_starfield_handle();
|
let s = input.ct.get_starfield_handle();
|
||||||
|
|
||||||
// Update global values
|
// Update global values
|
||||||
@ -77,7 +81,10 @@ impl super::GPUState {
|
|||||||
|
|
||||||
// Write all new particles to GPU buffer
|
// Write all new particles to GPU buffer
|
||||||
for i in input.particles.iter() {
|
for i in input.particles.iter() {
|
||||||
self.state.push_particle_buffer(ParticleInstance {
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.vertex_buffers.particle.instances,
|
||||||
|
ParticleInstance::SIZE * self.state.vertex_buffers.particle_counter,
|
||||||
|
bytemuck::cast_slice(&[ParticleInstance {
|
||||||
position: [i.pos.x, i.pos.y],
|
position: [i.pos.x, i.pos.y],
|
||||||
velocity: i.velocity.into(),
|
velocity: i.velocity.into(),
|
||||||
angle: i.angle,
|
angle: i.angle,
|
||||||
@ -87,7 +94,12 @@ impl super::GPUState {
|
|||||||
created: input.current_time,
|
created: input.current_time,
|
||||||
expires: input.current_time + i.lifetime,
|
expires: input.current_time + i.lifetime,
|
||||||
fade: i.fade,
|
fade: i.fade,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
self.state.vertex_buffers.particle_counter += 1;
|
||||||
|
if self.state.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT {
|
||||||
|
self.state.vertex_buffers.particle_counter = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create sprite instances
|
// Create sprite instances
|
||||||
@ -97,6 +109,10 @@ impl super::GPUState {
|
|||||||
let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom;
|
let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom;
|
||||||
let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom;
|
let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom;
|
||||||
|
|
||||||
|
// TODO: sorting. We don't need to sort ships, but we do need to sort system objects by z-level
|
||||||
|
// (which we don't yet draw)
|
||||||
|
// that should probably be done in iter_system().
|
||||||
|
|
||||||
// Order matters, it determines what is drawn on top.
|
// Order matters, it determines what is drawn on top.
|
||||||
// The order inside ships and projectiles doesn't matter,
|
// The order inside ships and projectiles doesn't matter,
|
||||||
// but ships should always be under projectiles.
|
// but ships should always be under projectiles.
|
||||||
@ -113,31 +129,31 @@ impl super::GPUState {
|
|||||||
// Starfield pipeline
|
// Starfield pipeline
|
||||||
self.state
|
self.state
|
||||||
.vertex_buffers
|
.vertex_buffers
|
||||||
.get_starfield()
|
.starfield
|
||||||
.set_in_pass(&mut render_pass);
|
.set_in_pass(&mut render_pass);
|
||||||
render_pass.set_pipeline(&self.starfield_pipeline);
|
render_pass.set_pipeline(&self.starfield_pipeline);
|
||||||
render_pass.draw_indexed(
|
render_pass.draw_indexed(
|
||||||
0..SPRITE_INDICES.len() as u32,
|
0..SPRITE_INDICES.len() as u32,
|
||||||
0,
|
0,
|
||||||
0..self.state.get_starfield_counter(),
|
0..self.starfield.instance_count,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sprite pipeline
|
// Sprite pipeline
|
||||||
self.state
|
self.state
|
||||||
.vertex_buffers
|
.vertex_buffers
|
||||||
.get_object()
|
.object
|
||||||
.set_in_pass(&mut render_pass);
|
.set_in_pass(&mut render_pass);
|
||||||
render_pass.set_pipeline(&self.object_pipeline);
|
render_pass.set_pipeline(&self.object_pipeline);
|
||||||
render_pass.draw_indexed(
|
render_pass.draw_indexed(
|
||||||
0..SPRITE_INDICES.len() as u32,
|
0..SPRITE_INDICES.len() as u32,
|
||||||
0,
|
0,
|
||||||
0..self.state.get_object_counter(),
|
0..self.state.vertex_buffers.object_counter as _,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Particle pipeline
|
// Particle pipeline
|
||||||
self.state
|
self.state
|
||||||
.vertex_buffers
|
.vertex_buffers
|
||||||
.get_particle()
|
.particle
|
||||||
.set_in_pass(&mut render_pass);
|
.set_in_pass(&mut render_pass);
|
||||||
render_pass.set_pipeline(&self.particle_pipeline);
|
render_pass.set_pipeline(&self.particle_pipeline);
|
||||||
render_pass.draw_indexed(
|
render_pass.draw_indexed(
|
||||||
@ -147,31 +163,27 @@ impl super::GPUState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Ui pipeline
|
// Ui pipeline
|
||||||
self.state
|
self.state.vertex_buffers.ui.set_in_pass(&mut render_pass);
|
||||||
.vertex_buffers
|
|
||||||
.get_ui()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.ui_pipeline);
|
render_pass.set_pipeline(&self.ui_pipeline);
|
||||||
render_pass.draw_indexed(
|
render_pass.draw_indexed(
|
||||||
0..SPRITE_INDICES.len() as u32,
|
0..SPRITE_INDICES.len() as u32,
|
||||||
0,
|
0,
|
||||||
0..self.state.get_ui_counter(),
|
0..self.state.vertex_buffers.ui_counter as _,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Radial progress bars
|
// Radial progress bars
|
||||||
// TODO: do we need to do this every time?
|
// TODO: do we need to do this every time?
|
||||||
self.state
|
self.state
|
||||||
.vertex_buffers
|
.vertex_buffers
|
||||||
.get_radialbar()
|
.radialbar
|
||||||
.set_in_pass(&mut render_pass);
|
.set_in_pass(&mut render_pass);
|
||||||
render_pass.set_pipeline(&self.radialbar_pipeline);
|
render_pass.set_pipeline(&self.radialbar_pipeline);
|
||||||
render_pass.draw_indexed(
|
render_pass.draw_indexed(
|
||||||
0..SPRITE_INDICES.len() as u32,
|
0..SPRITE_INDICES.len() as u32,
|
||||||
0,
|
0,
|
||||||
0..self.state.get_radialbar_counter(),
|
0..self.state.vertex_buffers.radialbar_counter as _,
|
||||||
);
|
);
|
||||||
|
|
||||||
let textareas = self.ui.get_textareas(&input, &self.state);
|
|
||||||
self.state
|
self.state
|
||||||
.text_renderer
|
.text_renderer
|
||||||
.prepare(
|
.prepare(
|
||||||
@ -183,7 +195,7 @@ impl super::GPUState {
|
|||||||
width: self.state.window_size.width,
|
width: self.state.window_size.width,
|
||||||
height: self.state.window_size.height,
|
height: self.state.window_size.height,
|
||||||
},
|
},
|
||||||
textareas,
|
self.ui.get_textareas(),
|
||||||
&mut self.state.text_cache,
|
&mut self.state.text_cache,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -7,24 +7,20 @@
|
|||||||
//! and the only one external code should interact with.
|
//! and the only one external code should interact with.
|
||||||
//! (Excluding data structs, like [`ObjectSprite`])
|
//! (Excluding data structs, like [`ObjectSprite`])
|
||||||
|
|
||||||
|
mod anchoredposition;
|
||||||
|
mod datastructs;
|
||||||
mod globaluniform;
|
mod globaluniform;
|
||||||
mod gpustate;
|
mod gpustate;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod positionanchor;
|
|
||||||
mod renderinput;
|
|
||||||
mod renderstate;
|
|
||||||
mod shaderprocessor;
|
mod shaderprocessor;
|
||||||
mod starfield;
|
mod starfield;
|
||||||
mod texturearray;
|
mod texturearray;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod vertexbuffer;
|
mod vertexbuffer;
|
||||||
|
|
||||||
use renderstate::*;
|
pub use anchoredposition::PositionAnchor;
|
||||||
|
pub use datastructs::RenderInput;
|
||||||
pub use gpustate::GPUState;
|
pub use gpustate::GPUState;
|
||||||
pub use positionanchor::PositionAnchor;
|
|
||||||
pub use renderinput::RenderInput;
|
|
||||||
|
|
||||||
use nalgebra::Matrix4;
|
use nalgebra::Matrix4;
|
||||||
|
|
||||||
/// Shader entry points
|
/// Shader entry points
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
use galactica_content::{Content, SystemHandle};
|
|
||||||
use galactica_playeragent::PlayerAgent;
|
|
||||||
use galactica_system::phys::{ParticleBuilder, PhysSim};
|
|
||||||
use galactica_util::timing::Timing;
|
|
||||||
use nalgebra::Vector2;
|
|
||||||
|
|
||||||
/// Bundles parameters passed to a single call to GPUState::render
|
|
||||||
pub struct RenderInput<'a> {
|
|
||||||
/// Camera position, in world units
|
|
||||||
pub camera_pos: Vector2<f32>,
|
|
||||||
|
|
||||||
/// Player ship data
|
|
||||||
pub player: &'a PlayerAgent,
|
|
||||||
|
|
||||||
/// The system we're currently in
|
|
||||||
pub current_system: SystemHandle,
|
|
||||||
|
|
||||||
/// Height of screen, in world units
|
|
||||||
pub camera_zoom: f32,
|
|
||||||
|
|
||||||
/// The world state to render
|
|
||||||
pub systemsim: &'a PhysSim,
|
|
||||||
|
|
||||||
// TODO: handle overflow. is it a problem?
|
|
||||||
/// The current time, in seconds
|
|
||||||
pub current_time: f32,
|
|
||||||
|
|
||||||
/// Game content
|
|
||||||
pub ct: &'a Content,
|
|
||||||
|
|
||||||
/// Particles to spawn during this frame
|
|
||||||
pub particles: &'a Vec<ParticleBuilder>,
|
|
||||||
|
|
||||||
/// Time we spent in each part of the game loop
|
|
||||||
pub timing: Timing,
|
|
||||||
}
|
|
@ -1,237 +0,0 @@
|
|||||||
use galactica_content::Content;
|
|
||||||
use galactica_util::constants::{
|
|
||||||
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, RADIALBAR_SPRITE_INSTANCE_LIMIT,
|
|
||||||
UI_SPRITE_INSTANCE_LIMIT,
|
|
||||||
};
|
|
||||||
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
|
||||||
use std::rc::Rc;
|
|
||||||
use wgpu::BufferAddress;
|
|
||||||
use winit::window::Window;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
globaluniform::GlobalUniform,
|
|
||||||
vertexbuffer::{
|
|
||||||
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
|
||||||
types::{
|
|
||||||
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
|
|
||||||
UiInstance,
|
|
||||||
},
|
|
||||||
BufferObject, VertexBuffer,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Vertex buffers
|
|
||||||
pub(crate) struct VertexBuffers {
|
|
||||||
// Keeps track of length of each buffer
|
|
||||||
// Most of these are reset on each frame.
|
|
||||||
//
|
|
||||||
// The exception is particle_counter, which
|
|
||||||
// is never reset, and loops back to zero once
|
|
||||||
// it exceeds buffer length
|
|
||||||
object_counter: BufferAddress,
|
|
||||||
ui_counter: BufferAddress,
|
|
||||||
particle_counter: BufferAddress,
|
|
||||||
radialbar_counter: BufferAddress,
|
|
||||||
starfield_counter: BufferAddress,
|
|
||||||
starfield_limit: BufferAddress,
|
|
||||||
|
|
||||||
object: Rc<VertexBuffer>,
|
|
||||||
starfield: Rc<VertexBuffer>,
|
|
||||||
ui: Rc<VertexBuffer>,
|
|
||||||
particle: Rc<VertexBuffer>,
|
|
||||||
radialbar: Rc<VertexBuffer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> VertexBuffers {
|
|
||||||
pub fn new(device: &wgpu::Device, ct: &Content) -> Self {
|
|
||||||
Self {
|
|
||||||
object_counter: 0,
|
|
||||||
ui_counter: 0,
|
|
||||||
particle_counter: 0,
|
|
||||||
radialbar_counter: 0,
|
|
||||||
starfield_counter: 0,
|
|
||||||
starfield_limit: ct.get_config().starfield_instance_limit,
|
|
||||||
|
|
||||||
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
|
|
||||||
"object",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
OBJECT_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
|
|
||||||
"starfield",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
ct.get_config().starfield_instance_limit,
|
|
||||||
)),
|
|
||||||
|
|
||||||
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
|
||||||
"ui",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
UI_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
|
|
||||||
"particle",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
PARTICLE_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
|
|
||||||
"radial bar",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
10,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_ui(&'a self) -> &'a Rc<VertexBuffer> {
|
|
||||||
&self.ui
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_object(&'a self) -> &'a Rc<VertexBuffer> {
|
|
||||||
&self.object
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_particle(&'a self) -> &'a Rc<VertexBuffer> {
|
|
||||||
&self.particle
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_radialbar(&'a self) -> &'a Rc<VertexBuffer> {
|
|
||||||
&self.radialbar
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_starfield(&'a self) -> &'a Rc<VertexBuffer> {
|
|
||||||
&self.starfield
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renderer state. A reference to this struct is often passed to helper functions.
|
|
||||||
pub(crate) struct RenderState {
|
|
||||||
pub window: Window,
|
|
||||||
pub window_size: winit::dpi::PhysicalSize<u32>,
|
|
||||||
pub window_aspect: f32,
|
|
||||||
|
|
||||||
pub queue: wgpu::Queue,
|
|
||||||
pub global_uniform: GlobalUniform,
|
|
||||||
pub vertex_buffers: VertexBuffers,
|
|
||||||
|
|
||||||
pub text_font_system: FontSystem,
|
|
||||||
pub text_cache: SwashCache,
|
|
||||||
pub text_atlas: TextAtlas,
|
|
||||||
pub text_renderer: TextRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderState {
|
|
||||||
/// Prepare this state for a new frame
|
|
||||||
pub fn frame_reset(&mut self) {
|
|
||||||
self.vertex_buffers.object_counter = 0;
|
|
||||||
self.vertex_buffers.ui_counter = 0;
|
|
||||||
self.vertex_buffers.radialbar_counter = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_ui_buffer(&mut self, instance: UiInstance) {
|
|
||||||
// Enforce buffer limit
|
|
||||||
if self.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
|
||||||
// TODO: no panic, handle this better.
|
|
||||||
panic!("UI limit exceeded!")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.ui.instances,
|
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
|
||||||
bytemuck::cast_slice(&[instance]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.ui_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_ui_counter(&self) -> u32 {
|
|
||||||
self.vertex_buffers.ui_counter as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_object_buffer(&mut self, instance: ObjectInstance) {
|
|
||||||
// Enforce buffer limit
|
|
||||||
if self.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
|
||||||
// TODO: no panic, handle this better.
|
|
||||||
panic!("Object limit exceeded!")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.object.instances,
|
|
||||||
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
|
|
||||||
bytemuck::cast_slice(&[instance]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.object_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_object_counter(&self) -> u32 {
|
|
||||||
self.vertex_buffers.object_counter as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_radialbar_buffer(&mut self, instance: RadialBarInstance) {
|
|
||||||
// Enforce buffer limit
|
|
||||||
if self.vertex_buffers.radialbar_counter as u64 > RADIALBAR_SPRITE_INSTANCE_LIMIT {
|
|
||||||
// TODO: no panic, handle this better.
|
|
||||||
panic!("Radialbar sprite limit exceeded!")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.radialbar.instances,
|
|
||||||
RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter,
|
|
||||||
bytemuck::cast_slice(&[instance]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.radialbar_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_radialbar_counter(&self) -> u32 {
|
|
||||||
self.vertex_buffers.radialbar_counter as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_starfield_counter(&mut self) {
|
|
||||||
self.vertex_buffers.starfield_counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_starfield_buffer(&mut self, instance: StarfieldInstance) {
|
|
||||||
// Enforce buffer limit
|
|
||||||
// This should never happen, since starfield generator checks array size
|
|
||||||
if self.vertex_buffers.starfield_counter as u64 > self.vertex_buffers.starfield_limit {
|
|
||||||
panic!("Starfield sprite limit exceeded!")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.starfield.instances,
|
|
||||||
StarfieldInstance::SIZE * self.vertex_buffers.starfield_counter,
|
|
||||||
bytemuck::cast_slice(&[instance]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.starfield_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_starfield_counter(&self) -> u32 {
|
|
||||||
self.vertex_buffers.starfield_counter as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_particle_buffer(&mut self, instance: ParticleInstance) {
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.particle.instances,
|
|
||||||
ParticleInstance::SIZE * self.vertex_buffers.particle_counter,
|
|
||||||
bytemuck::cast_slice(&[instance]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.particle_counter += 1;
|
|
||||||
if self.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT {
|
|
||||||
self.vertex_buffers.particle_counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//pub fn get_particle_counter(&self) -> u32 {
|
|
||||||
// self.vertex_buffers.particle_counter as u32
|
|
||||||
//}
|
|
||||||
}
|
|
@ -2,7 +2,10 @@ use galactica_content::Content;
|
|||||||
use nalgebra::{Point2, Point3, Vector2, Vector3};
|
use nalgebra::{Point2, Point3, Vector2, Vector3};
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use crate::{vertexbuffer::types::StarfieldInstance, RenderState};
|
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.
|
||||||
@ -20,11 +23,15 @@ pub(crate) struct StarfieldStar {
|
|||||||
|
|
||||||
pub(crate) struct Starfield {
|
pub(crate) struct Starfield {
|
||||||
stars: Vec<StarfieldStar>,
|
stars: Vec<StarfieldStar>,
|
||||||
|
pub instance_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Starfield {
|
impl Starfield {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { stars: Vec::new() }
|
Self {
|
||||||
|
stars: Vec::new(),
|
||||||
|
instance_count: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn regenerate(&mut self, ct: &Content) {
|
pub fn regenerate(&mut self, ct: &Content) {
|
||||||
@ -90,19 +97,24 @@ impl Starfield {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add all tiles to buffer
|
// Add all tiles to buffer
|
||||||
state.reset_starfield_counter();
|
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::new(sz * x as f32, sz * y as f32, 0.0);
|
let offset = Vector3::new(sz * x as f32, sz * y as f32, 0.0);
|
||||||
for s in &self.stars {
|
for s in &self.stars {
|
||||||
// This shouldn't ever panic.
|
state.queue.write_buffer(
|
||||||
// instance count is guaranteed to stay within buffer limits,
|
&state.vertex_buffers.starfield.instances,
|
||||||
// this is guaranteed by previous checks.
|
StarfieldInstance::SIZE * self.instance_count as u64,
|
||||||
state.push_starfield_buffer(StarfieldInstance {
|
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;
|
||||||
|
|
||||||
|
// instance_count is guaranteed to stay within buffer limits,
|
||||||
|
// this is guaranteed by previous checks.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use glyphon::{Attrs, Buffer, Color, Family, Metrics, Shaping, TextArea, TextBounds};
|
use glyphon::{Attrs, Buffer, Color, Family, Metrics, Shaping, TextArea, TextBounds};
|
||||||
|
|
||||||
use crate::{RenderInput, RenderState};
|
use crate::{datastructs::RenderState, RenderInput};
|
||||||
|
|
||||||
pub(super) struct FpsIndicator {
|
pub(super) struct FpsIndicator {
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
use galactica_content::Content;
|
|
||||||
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
|
||||||
use glyphon::TextArea;
|
use glyphon::TextArea;
|
||||||
|
|
||||||
use super::{fpsindicator::FpsIndicator, planet::Planet, radar::Radar, status::Status};
|
use super::{fpsindicator::FpsIndicator, radar::Radar, status::Status};
|
||||||
use crate::{RenderInput, RenderState};
|
use crate::{datastructs::RenderState, RenderInput};
|
||||||
|
|
||||||
pub struct UiManager {
|
pub struct UiManager {
|
||||||
radar: Radar,
|
radar: Radar,
|
||||||
status: Status,
|
status: Status,
|
||||||
fps: FpsIndicator,
|
fps: FpsIndicator,
|
||||||
planet: Planet,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiManager {
|
impl UiManager {
|
||||||
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
pub fn new(state: &mut RenderState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
planet: Planet::new(ct, state),
|
|
||||||
radar: Radar::new(),
|
radar: Radar::new(),
|
||||||
status: Status::new(),
|
status: Status::new(),
|
||||||
fps: FpsIndicator::new(state),
|
fps: FpsIndicator::new(state),
|
||||||
@ -24,48 +20,12 @@ impl UiManager {
|
|||||||
|
|
||||||
/// Draw all ui elements
|
/// Draw all ui elements
|
||||||
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
let ship_handle = input.player.ship.unwrap();
|
|
||||||
let ship = input
|
|
||||||
.systemsim
|
|
||||||
.get_ship(&PhysSimShipHandle(ship_handle))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.fps.update(input, state);
|
|
||||||
|
|
||||||
match ship.data.get_state() {
|
|
||||||
ShipState::Collapsing
|
|
||||||
| ShipState::Dead
|
|
||||||
| ShipState::Flying { .. }
|
|
||||||
| ShipState::Landing { .. }
|
|
||||||
| ShipState::UnLanding { .. } => {
|
|
||||||
self.radar.draw(input, state);
|
self.radar.draw(input, state);
|
||||||
self.status.draw(input, state);
|
self.status.draw(input, state);
|
||||||
|
self.fps.update(input, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShipState::Landed { .. } => {
|
pub fn get_textareas(&self) -> [TextArea; 1] {
|
||||||
self.planet.draw(input, state);
|
[self.fps.get_textarea()]
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_textareas(&self, input: &RenderInput, state: &RenderState) -> Vec<TextArea> {
|
|
||||||
let mut v = Vec::with_capacity(5);
|
|
||||||
|
|
||||||
let ship_handle = input.player.ship.unwrap();
|
|
||||||
let ship = input
|
|
||||||
.systemsim
|
|
||||||
.get_ship(&PhysSimShipHandle(ship_handle))
|
|
||||||
.unwrap();
|
|
||||||
match ship.data.get_state() {
|
|
||||||
ShipState::Collapsing
|
|
||||||
| ShipState::Dead
|
|
||||||
| ShipState::Flying { .. }
|
|
||||||
| ShipState::Landing { .. }
|
|
||||||
| ShipState::UnLanding { .. } => v.push(self.fps.get_textarea()),
|
|
||||||
|
|
||||||
ShipState::Landed { .. } => v.extend(self.planet.get_textarea(input, state)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
mod fpsindicator;
|
mod fpsindicator;
|
||||||
mod manager;
|
mod manager;
|
||||||
mod planet;
|
|
||||||
mod radar;
|
mod radar;
|
||||||
mod status;
|
mod status;
|
||||||
mod util;
|
|
||||||
|
|
||||||
pub use manager::UiManager;
|
pub use manager::UiManager;
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
use galactica_content::{Content, SystemObject, SystemObjectHandle};
|
|
||||||
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
|
||||||
use galactica_util::to_radians;
|
|
||||||
use glyphon::{cosmic_text::Align, Attrs, Color, Metrics, TextArea, Weight};
|
|
||||||
use nalgebra::{Point2, Vector2};
|
|
||||||
|
|
||||||
use super::util::{SpriteRect, UiImage, UiTextArea};
|
|
||||||
use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderInput, RenderState};
|
|
||||||
|
|
||||||
pub(super) struct Planet {
|
|
||||||
// UI elements
|
|
||||||
planet_desc: UiTextArea,
|
|
||||||
planet_name: UiTextArea,
|
|
||||||
landscape: UiImage,
|
|
||||||
|
|
||||||
// Height of whole element,in logical pixels
|
|
||||||
size: f32,
|
|
||||||
|
|
||||||
/// What object we're displaying currently.
|
|
||||||
/// Whenever this changes, we need to reflow text.
|
|
||||||
current_object: Option<SystemObjectHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: no scroll
|
|
||||||
// TODO: animate in/out
|
|
||||||
|
|
||||||
impl Planet {
|
|
||||||
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
|
||||||
let size = 800.0;
|
|
||||||
let s = Self {
|
|
||||||
// height of element in logical pixels
|
|
||||||
size,
|
|
||||||
current_object: None,
|
|
||||||
|
|
||||||
planet_desc: UiTextArea::new(
|
|
||||||
state,
|
|
||||||
ct.get_sprite_handle("ui::planet"),
|
|
||||||
Point2::new(0.0, 0.0),
|
|
||||||
size,
|
|
||||||
SpriteRect {
|
|
||||||
pos: Point2::new(25.831, 284.883) / 512.0,
|
|
||||||
dim: Vector2::new(433.140, 97.220) / 512.0,
|
|
||||||
},
|
|
||||||
Metrics::new(16.0, 18.0),
|
|
||||||
Color::rgb(255, 255, 255),
|
|
||||||
Align::Left,
|
|
||||||
),
|
|
||||||
|
|
||||||
planet_name: UiTextArea::new(
|
|
||||||
state,
|
|
||||||
ct.get_sprite_handle("ui::planet"),
|
|
||||||
Point2::new(0.0, 0.0),
|
|
||||||
size,
|
|
||||||
SpriteRect {
|
|
||||||
pos: Point2::new(165.506, 82.0) / 512.0,
|
|
||||||
dim: Vector2::new(74.883, 17.0) / 512.0,
|
|
||||||
},
|
|
||||||
Metrics::new(19.0, 19.0),
|
|
||||||
Color::rgb(255, 255, 255),
|
|
||||||
Align::Center,
|
|
||||||
),
|
|
||||||
|
|
||||||
landscape: UiImage::new(
|
|
||||||
ct.get_sprite_handle("ui::planet"),
|
|
||||||
Point2::new(0.0, 0.0),
|
|
||||||
size,
|
|
||||||
ct.get_sprite_handle("ui::landscape::test"),
|
|
||||||
ct.get_sprite_handle("ui::landscapemask"),
|
|
||||||
SpriteRect {
|
|
||||||
pos: Point2::new(32.031, 75.587) / 512.0,
|
|
||||||
dim: Vector2::new(342.811, 171.402) / 512.0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Planet {
|
|
||||||
fn reflow(&mut self, planet: &SystemObject, state: &mut RenderState) {
|
|
||||||
self.planet_desc.set_text(
|
|
||||||
state,
|
|
||||||
&planet.desc,
|
|
||||||
Attrs::new()
|
|
||||||
.weight(Weight::NORMAL)
|
|
||||||
.family(glyphon::Family::SansSerif),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.planet_name.set_text(
|
|
||||||
state,
|
|
||||||
&planet.name,
|
|
||||||
Attrs::new()
|
|
||||||
.weight(Weight::BOLD)
|
|
||||||
.family(glyphon::Family::Serif),
|
|
||||||
);
|
|
||||||
self.current_object = Some(planet.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
|
||||||
// Get required data
|
|
||||||
let ship_handle = input.player.ship.unwrap();
|
|
||||||
let ship_data = input
|
|
||||||
.systemsim
|
|
||||||
.get_ship(&PhysSimShipHandle(ship_handle))
|
|
||||||
.unwrap();
|
|
||||||
let planet_handle = match ship_data.data.get_state() {
|
|
||||||
ShipState::Landed { target } => *target,
|
|
||||||
_ => unreachable!("tried to draw planet interface while not landed!"),
|
|
||||||
};
|
|
||||||
let planet = input.ct.get_system_object(planet_handle);
|
|
||||||
|
|
||||||
// Reconfigure for new planet if necessary
|
|
||||||
if self
|
|
||||||
.current_object
|
|
||||||
.map(|x| x != planet_handle)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
self.reflow(planet, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw elements
|
|
||||||
self.landscape.push_to_buffer(state);
|
|
||||||
state.push_ui_buffer(UiInstance {
|
|
||||||
anchor: PositionAnchor::CC.to_int(),
|
|
||||||
position: [0.0, 0.0],
|
|
||||||
angle: to_radians(90.0),
|
|
||||||
size: self.size,
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
sprite_index: input.ct.get_sprite_handle("ui::planet").get_index(),
|
|
||||||
mask_index: [0, 0],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_textarea(&self, _input: &RenderInput, state: &RenderState) -> [TextArea; 2] {
|
|
||||||
[
|
|
||||||
self.planet_desc.get_textarea(state),
|
|
||||||
self.planet_name.get_textarea(state),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,12 @@
|
|||||||
use galactica_system::data::ShipState;
|
use galactica_system::data::ShipState;
|
||||||
use galactica_util::{clockwise_angle, to_radians};
|
use galactica_util::{clockwise_angle, constants::UI_SPRITE_INSTANCE_LIMIT, to_radians};
|
||||||
use nalgebra::{Point2, Rotation2, Vector2};
|
use nalgebra::{Point2, Rotation2, Vector2};
|
||||||
|
|
||||||
use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderInput, RenderState};
|
use crate::{
|
||||||
|
datastructs::RenderState,
|
||||||
|
vertexbuffer::{types::UiInstance, BufferObject},
|
||||||
|
PositionAnchor, RenderInput,
|
||||||
|
};
|
||||||
|
|
||||||
pub(super) struct Radar {
|
pub(super) struct Radar {
|
||||||
last_player_position: Point2<f32>,
|
last_player_position: Point2<f32>,
|
||||||
@ -60,16 +64,26 @@ impl Radar {
|
|||||||
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
|
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
|
||||||
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
|
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
|
||||||
|
|
||||||
|
// Enforce buffer limit
|
||||||
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
state.push_ui_buffer(UiInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwNw.to_int(),
|
anchor: PositionAnchor::NwNw.to_int(),
|
||||||
position: [10.0, -10.0],
|
position: [10.0, -10.0],
|
||||||
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.ct.get_sprite_handle("ui::radar").get_index(),
|
sprite_index: input.ct.get_sprite_handle("ui::radar").get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
// Draw system objects
|
// Draw system objects
|
||||||
let system = input.ct.get_system(input.current_system);
|
let system = input.ct.get_system(input.current_system);
|
||||||
@ -87,8 +101,17 @@ impl Radar {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce buffer limit
|
||||||
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
state.push_ui_buffer(UiInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: (Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
|
position: (Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
|
||||||
+ (d * (radar_size / 2.0)))
|
+ (d * (radar_size / 2.0)))
|
||||||
@ -97,9 +120,10 @@ impl Radar {
|
|||||||
size,
|
size,
|
||||||
color: [0.5, 0.5, 0.5, 1.0],
|
color: [0.5, 0.5, 0.5, 1.0],
|
||||||
sprite_index: planet_sprite.get_index(),
|
sprite_index: planet_sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
})
|
);
|
||||||
};
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw ships
|
// Draw ships
|
||||||
@ -140,19 +164,32 @@ impl Radar {
|
|||||||
if size < 2.0 {
|
if size < 2.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let angle = r.rotation().angle();
|
||||||
|
|
||||||
let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
|
let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
|
||||||
+ (d * (radar_size / 2.0));
|
+ (d * (radar_size / 2.0));
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
// Enforce buffer limit
|
||||||
|
// TODO: cleaner solution. don't do this everywhere.
|
||||||
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push this object's instance
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: position.into(),
|
position: position.into(),
|
||||||
angle: r.rotation().angle(),
|
angle: -angle, // TODO: consistent angles
|
||||||
size,
|
size,
|
||||||
color,
|
color,
|
||||||
sprite_index: ship_sprite.get_index(),
|
sprite_index: ship_sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +205,16 @@ impl Radar {
|
|||||||
let sprite = input.ct.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);
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
// Enforce buffer limit (this section adds four items)
|
||||||
|
if state.vertex_buffers.ui_counter as u64 + 4 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwNw.to_int(),
|
anchor: PositionAnchor::NwNw.to_int(),
|
||||||
position: Point2::new(
|
position: Point2::new(
|
||||||
(radar_size / 2.0 + 10.0) - d.x,
|
(radar_size / 2.0 + 10.0) - d.x,
|
||||||
@ -179,10 +225,14 @@ impl Radar {
|
|||||||
size,
|
size,
|
||||||
color,
|
color,
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwSw.to_int(),
|
anchor: PositionAnchor::NwSw.to_int(),
|
||||||
position: Point2::new(
|
position: Point2::new(
|
||||||
(radar_size / 2.0 + 10.0) - d.x,
|
(radar_size / 2.0 + 10.0) - d.x,
|
||||||
@ -193,10 +243,14 @@ impl Radar {
|
|||||||
size,
|
size,
|
||||||
color,
|
color,
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwSe.to_int(),
|
anchor: PositionAnchor::NwSe.to_int(),
|
||||||
position: Point2::new(
|
position: Point2::new(
|
||||||
(radar_size / 2.0 + 10.0) + d.x,
|
(radar_size / 2.0 + 10.0) + d.x,
|
||||||
@ -207,10 +261,14 @@ impl Radar {
|
|||||||
size,
|
size,
|
||||||
color,
|
color,
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwNe.to_int(),
|
anchor: PositionAnchor::NwNe.to_int(),
|
||||||
position: Point2::new(
|
position: Point2::new(
|
||||||
(radar_size / 2.0 + 10.0) + d.x,
|
(radar_size / 2.0 + 10.0) + d.x,
|
||||||
@ -221,8 +279,9 @@ impl Radar {
|
|||||||
size,
|
size,
|
||||||
color,
|
color,
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrow to center of system
|
// Arrow to center of system
|
||||||
@ -233,15 +292,24 @@ impl Radar {
|
|||||||
let position = Point2::new(10.0 + (radar_size / 2.0), -10.0 - (radar_size / 2.0))
|
let position = Point2::new(10.0 + (radar_size / 2.0), -10.0 - (radar_size / 2.0))
|
||||||
+ Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0);
|
+ Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0);
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: position.into(),
|
position: position.into(),
|
||||||
angle,
|
angle,
|
||||||
size: 10.0,
|
size: 10.0,
|
||||||
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
|
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
|
||||||
sprite_index: arrow_sprite.get_index(),
|
sprite_index: arrow_sprite.get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
use galactica_system::data::ShipState;
|
use galactica_system::data::ShipState;
|
||||||
|
use galactica_util::constants::{RADIALBAR_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT};
|
||||||
use std::f32::consts::TAU;
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vertexbuffer::types::{RadialBarInstance, UiInstance},
|
datastructs::RenderState,
|
||||||
PositionAnchor, RenderInput, RenderState,
|
vertexbuffer::{
|
||||||
|
types::{RadialBarInstance, UiInstance},
|
||||||
|
BufferObject,
|
||||||
|
},
|
||||||
|
PositionAnchor, RenderInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) struct Status {}
|
pub(super) struct Status {}
|
||||||
@ -15,6 +20,11 @@ impl Status {
|
|||||||
|
|
||||||
impl Status {
|
impl Status {
|
||||||
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
let max_shields;
|
let max_shields;
|
||||||
let current_shields;
|
let current_shields;
|
||||||
let current_hull;
|
let current_hull;
|
||||||
@ -45,32 +55,52 @@ impl Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
position: [-10.0, -10.0],
|
position: [-10.0, -10.0],
|
||||||
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.ct.get_sprite_handle("ui::status").get_index(),
|
sprite_index: input.ct.get_sprite_handle("ui::status").get_index(),
|
||||||
mask_index: [0, 0],
|
}]),
|
||||||
});
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
state.push_radialbar_buffer(RadialBarInstance {
|
// We add two items here, so +2
|
||||||
|
if state.vertex_buffers.radialbar_counter as u64 + 2 > RADIALBAR_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Radialbar limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.radialbar.instances,
|
||||||
|
RadialBarInstance::SIZE * state.vertex_buffers.radialbar_counter,
|
||||||
|
bytemuck::cast_slice(&[RadialBarInstance {
|
||||||
position: [-19.0, -19.0],
|
position: [-19.0, -19.0],
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
diameter: 182.0,
|
diameter: 182.0,
|
||||||
stroke: 5.0,
|
stroke: 5.0,
|
||||||
color: [0.3, 0.6, 0.8, 1.0],
|
color: [0.3, 0.6, 0.8, 1.0],
|
||||||
angle: (current_shields / max_shields) * TAU,
|
angle: (current_shields / max_shields) * TAU,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
state.vertex_buffers.radialbar_counter += 1;
|
||||||
|
|
||||||
state.push_radialbar_buffer(RadialBarInstance {
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.radialbar.instances,
|
||||||
|
RadialBarInstance::SIZE * state.vertex_buffers.radialbar_counter,
|
||||||
|
bytemuck::cast_slice(&[RadialBarInstance {
|
||||||
position: [-27.0, -27.0],
|
position: [-27.0, -27.0],
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
diameter: 166.0,
|
diameter: 166.0,
|
||||||
stroke: 5.0,
|
stroke: 5.0,
|
||||||
color: [0.8, 0.7, 0.5, 1.0],
|
color: [0.8, 0.7, 0.5, 1.0],
|
||||||
angle: (current_hull / max_hull) * TAU,
|
angle: (current_hull / max_hull) * TAU,
|
||||||
});
|
}]),
|
||||||
|
);
|
||||||
|
state.vertex_buffers.radialbar_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
use galactica_content::SpriteHandle;
|
|
||||||
use galactica_util::to_radians;
|
|
||||||
use nalgebra::{Point2, Vector2};
|
|
||||||
|
|
||||||
use super::SpriteRect;
|
|
||||||
use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderState};
|
|
||||||
|
|
||||||
pub struct UiImage {
|
|
||||||
parent: SpriteHandle,
|
|
||||||
parent_position: Point2<f32>,
|
|
||||||
parent_size: f32,
|
|
||||||
|
|
||||||
inner: SpriteHandle,
|
|
||||||
mask: SpriteHandle,
|
|
||||||
rect: SpriteRect,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiImage {
|
|
||||||
pub fn new(
|
|
||||||
parent: SpriteHandle,
|
|
||||||
parent_position: Point2<f32>,
|
|
||||||
parent_size: f32,
|
|
||||||
inner: SpriteHandle,
|
|
||||||
mask: SpriteHandle,
|
|
||||||
rect: SpriteRect,
|
|
||||||
) -> Self {
|
|
||||||
return Self {
|
|
||||||
parent,
|
|
||||||
parent_position,
|
|
||||||
parent_size,
|
|
||||||
inner,
|
|
||||||
mask,
|
|
||||||
rect,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add this image to the gpu sprite buffer
|
|
||||||
pub fn push_to_buffer(&self, state: &mut RenderState) {
|
|
||||||
let h = self.parent_size;
|
|
||||||
let w = self.parent.aspect * h;
|
|
||||||
|
|
||||||
let zero = Point2::new(
|
|
||||||
self.parent_position.x - (w / 2.0),
|
|
||||||
self.parent_position.y + (h / 2.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let pos = zero + Vector2::new(self.rect.pos.x * w, -self.rect.pos.y * h);
|
|
||||||
let dim = Vector2::new(self.rect.dim.x * w, self.rect.dim.y * h);
|
|
||||||
|
|
||||||
state.push_ui_buffer(UiInstance {
|
|
||||||
anchor: PositionAnchor::CNw.to_int(),
|
|
||||||
position: pos.into(),
|
|
||||||
angle: to_radians(90.0),
|
|
||||||
size: dim.y,
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
sprite_index: self.inner.get_index(),
|
|
||||||
mask_index: [1, self.mask.get_index()],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
mod image;
|
|
||||||
mod textarea;
|
|
||||||
|
|
||||||
pub(super) use image::UiImage;
|
|
||||||
pub(super) use textarea::UiTextArea;
|
|
||||||
|
|
||||||
use nalgebra::{Point2, Vector2};
|
|
||||||
|
|
||||||
/// Represents a rectangular region inside a sprite.
|
|
||||||
pub(crate) struct SpriteRect {
|
|
||||||
/// The position of the top-left corner of this rectangle, in fractional units.
|
|
||||||
/// (0.0 is left edge of sprite, 1.0 is right edge)
|
|
||||||
pub pos: Point2<f32>,
|
|
||||||
|
|
||||||
/// The width and height of this rectangle, in fractional units.
|
|
||||||
/// 1.0 will be as tall as the sprite, 0.5 will be half as tall
|
|
||||||
pub dim: Vector2<f32>,
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
use galactica_content::SpriteHandle;
|
|
||||||
use glyphon::{cosmic_text::Align, Attrs, Buffer, Color, Metrics, Shaping, TextArea, TextBounds};
|
|
||||||
use nalgebra::{Point2, Vector2};
|
|
||||||
|
|
||||||
use super::SpriteRect;
|
|
||||||
use crate::RenderState;
|
|
||||||
|
|
||||||
/// Represents a text area inside a sprite.
|
|
||||||
pub(crate) struct UiTextArea {
|
|
||||||
/// Parent sprite
|
|
||||||
sprite: SpriteHandle,
|
|
||||||
|
|
||||||
/// Position of parent sprite's center, in logical pixels,
|
|
||||||
/// with 0, 0 at the center of the screen
|
|
||||||
sprite_position: Point2<f32>,
|
|
||||||
|
|
||||||
/// Height of parent sprite, in logical pixels
|
|
||||||
sprite_size: f32,
|
|
||||||
|
|
||||||
/// Bounds of text area
|
|
||||||
rect: SpriteRect,
|
|
||||||
|
|
||||||
/// Text buffer
|
|
||||||
buffer: Buffer,
|
|
||||||
|
|
||||||
/// Text color
|
|
||||||
color: Color,
|
|
||||||
|
|
||||||
/// Text alignment
|
|
||||||
align: Align,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiTextArea {
|
|
||||||
pub fn new(
|
|
||||||
state: &mut RenderState,
|
|
||||||
sprite: SpriteHandle,
|
|
||||||
sprite_position: Point2<f32>,
|
|
||||||
sprite_size: f32,
|
|
||||||
rect: SpriteRect,
|
|
||||||
text_metrics: Metrics,
|
|
||||||
color: Color,
|
|
||||||
align: Align,
|
|
||||||
) -> Self {
|
|
||||||
let mut s = Self {
|
|
||||||
buffer: Buffer::new(&mut state.text_font_system, text_metrics),
|
|
||||||
sprite_size: f32::NAN,
|
|
||||||
sprite,
|
|
||||||
sprite_position,
|
|
||||||
rect,
|
|
||||||
align,
|
|
||||||
color,
|
|
||||||
};
|
|
||||||
s.set_size(state, sprite_size);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_size(&mut self, state: &mut RenderState, sprite_size: f32) {
|
|
||||||
self.sprite_size = sprite_size;
|
|
||||||
self.buffer.set_size(
|
|
||||||
&mut state.text_font_system,
|
|
||||||
(self.rect.dim.x * self.sprite_size) * state.window.scale_factor() as f32,
|
|
||||||
(self.rect.dim.y * self.sprite_size * self.sprite.aspect)
|
|
||||||
* state.window.scale_factor() as f32,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_text(&mut self, state: &mut RenderState, text: &str, attrs: Attrs) {
|
|
||||||
self.buffer
|
|
||||||
.set_text(&mut state.text_font_system, text, attrs, Shaping::Advanced);
|
|
||||||
|
|
||||||
for l in &mut self.buffer.lines {
|
|
||||||
l.set_align(Some(self.align));
|
|
||||||
}
|
|
||||||
self.buffer.shape_until_scroll(&mut state.text_font_system);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_textarea(&self, state: &RenderState) -> TextArea {
|
|
||||||
let h = self.sprite_size;
|
|
||||||
let w = self.sprite.aspect * h;
|
|
||||||
|
|
||||||
// Glypon works with physical pixels, so we must convert
|
|
||||||
let fac = state.window.scale_factor() as f32;
|
|
||||||
|
|
||||||
// All the units below are in logical pixels
|
|
||||||
let zero = Vector2::new(
|
|
||||||
(state.window_size.width as f32 / (2.0 * fac)) - (w / 2.0) + self.sprite_position.x,
|
|
||||||
(state.window_size.height as f32 / (2.0 * fac)) - (h / 2.0) - self.sprite_position.y,
|
|
||||||
);
|
|
||||||
let corner_ne = zero + Vector2::new(self.rect.pos.x * w, self.rect.pos.y * h);
|
|
||||||
let corner_sw = corner_ne + Vector2::new(self.rect.dim.x * w, self.rect.dim.y * h);
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
buffer: &self.buffer,
|
|
||||||
top: corner_ne.y * fac,
|
|
||||||
left: corner_ne.x * fac,
|
|
||||||
scale: 1.0,
|
|
||||||
bounds: TextBounds {
|
|
||||||
top: (corner_ne.y * fac) as i32,
|
|
||||||
bottom: (corner_sw.y * fac) as i32,
|
|
||||||
left: (corner_ne.x * fac) as i32,
|
|
||||||
right: (corner_sw.x * fac) as i32,
|
|
||||||
},
|
|
||||||
default_color: self.color,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -140,11 +140,6 @@ pub struct UiInstance {
|
|||||||
|
|
||||||
/// What texture to use for this sprite
|
/// What texture to use for this sprite
|
||||||
pub sprite_index: u32,
|
pub sprite_index: u32,
|
||||||
|
|
||||||
/// What mask to use for this sprite
|
|
||||||
/// If the first element is not 1, no mask is used.
|
|
||||||
/// Second element is sprite index of mask.
|
|
||||||
pub mask_index: [u32; 2],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferObject for UiInstance {
|
impl BufferObject for UiInstance {
|
||||||
@ -192,12 +187,6 @@ impl BufferObject for UiInstance {
|
|||||||
shader_location: 7,
|
shader_location: 7,
|
||||||
format: wgpu::VertexFormat::Uint32,
|
format: wgpu::VertexFormat::Uint32,
|
||||||
},
|
},
|
||||||
// Mask
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 10]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 8,
|
|
||||||
format: wgpu::VertexFormat::Uint32x2,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user