Compare commits

..

2 Commits

Author SHA1 Message Date
Mark 381dd4a383
Added dynamic radar colors 2024-01-02 10:12:10 -08:00
Mark 2e6f79ea31
Moved ui rendering to pipeline 2024-01-02 09:23:38 -08:00
13 changed files with 271 additions and 76 deletions

View File

@ -4,7 +4,7 @@
- Sound system - Sound system
- Particles, impact effects - Particles, impact effects
- Debris on ship death - Debris on ship death
- Radar: dynamic colors, size, planets and suns - Radar: ship size, planets and suns
---------------------------------- ----------------------------------

2
assets

@ -1 +1 @@
Subproject commit 91377416617d049acec68bb5a17647dcac7acd0e Subproject commit 62fd7fc297a2631691b588b898694e220d58cf78

View File

@ -1,7 +1,9 @@
[faction."player"] [faction."player"]
display_name = "Player" display_name = "Player"
relationship.enemy = "hostile" relationship.enemy = "hostile"
color = [0.0, 1.0, 0.0]
[faction."enemy"] [faction."enemy"]
display_name = "Enemy" display_name = "Enemy"
relationship.player = "hostile" relationship.player = "hostile"
color = [1.0, 0.0, 0.0]

View File

@ -44,8 +44,11 @@ pub const CONTENT_ROOT: &'static str = "./content";
/// Root directory of game textures /// Root directory of game textures
pub const TEXTURE_ROOT: &'static str = "./assets/render"; pub const TEXTURE_ROOT: &'static str = "./assets/render";
/// We can draw at most this many sprites on the screen. /// We can draw at most this many object sprites on the screen.
pub const SPRITE_INSTANCE_LIMIT: u64 = 500; pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500;
/// We can draw at most this many ui sprites on the screen.
pub const UI_SPRITE_INSTANCE_LIMIT: u64 = 100;
/// Must be small enough to fit in an i32 /// Must be small enough to fit in an i32
pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24; pub const STARFIELD_SPRITE_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;

View File

@ -14,6 +14,7 @@ pub(crate) mod syntax {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Faction { pub struct Faction {
pub display_name: String, pub display_name: String,
pub color: [f32; 3],
pub relationship: HashMap<String, super::Relationship>, pub relationship: HashMap<String, super::Relationship>,
} }
} }
@ -45,6 +46,10 @@ pub struct Faction {
/// The name of this faction /// The name of this faction
pub name: String, pub name: String,
/// This faction's color.
/// Format is RGB, with each color between 0 and 1.
pub color: [f32; 3],
/// This faction's handle /// This faction's handle
pub handle: FactionHandle, pub handle: FactionHandle,
@ -98,6 +103,7 @@ impl crate::Build for Faction {
name: faction_name.to_owned(), name: faction_name.to_owned(),
handle: h, handle: h,
relationships, relationships,
color: faction.color,
}); });
} }

View File

@ -0,0 +1,75 @@
struct InstanceInput {
@location(2) transform_matrix_0: vec4<f32>,
@location(3) transform_matrix_1: vec4<f32>,
@location(4) transform_matrix_2: vec4<f32>,
@location(5) transform_matrix_3: vec4<f32>,
@location(6) color_transform: vec4<f32>,
@location(7) texture_idx: u32,
};
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) texture_coords: vec2<f32>,
}
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) texture_coords: vec2<f32>,
@location(1) index: u32,
@location(2) color_transform: vec4<f32>,
}
@group(1) @binding(0)
var<uniform> global: GlobalUniform;
struct GlobalUniform {
camera_position: vec2<f32>,
camera_zoom: vec2<f32>,
camera_zoom_limits: vec2<f32>,
window_size: vec2<f32>,
window_aspect: vec2<f32>,
starfield_texture: vec2<u32>,
starfield_tile_size: vec2<f32>,
starfield_size_limits: vec2<f32>,
};
@group(0) @binding(0)
var texture_array: binding_array<texture_2d<f32>>;
@group(0) @binding(1)
var sampler_array: binding_array<sampler>;
@vertex
fn vertex_main(
vertex: VertexInput,
instance: InstanceInput,
) -> VertexOutput {
let transform = mat4x4<f32>(
instance.transform_matrix_0,
instance.transform_matrix_1,
instance.transform_matrix_2,
instance.transform_matrix_3,
);
var out: VertexOutput;
out.position = transform * vec4<f32>(vertex.position, 1.0);
out.texture_coords = vertex.texture_coords;
out.index = instance.texture_idx;
out.color_transform = instance.color_transform;
return out;
}
@fragment
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSampleLevel(
texture_array[in.index],
sampler_array[0],
in.texture_coords,
0.0
).rgba * in.color_transform;
}

View File

@ -15,7 +15,7 @@ use crate::{
texturearray::TextureArray, texturearray::TextureArray,
vertexbuffer::{ vertexbuffer::{
consts::{SPRITE_INDICES, SPRITE_VERTICES}, consts::{SPRITE_INDICES, SPRITE_VERTICES},
types::{SpriteInstance, StarfieldInstance, TexturedVertex}, types::{ObjectInstance, StarfieldInstance, TexturedVertex, UiInstance},
VertexBuffer, VertexBuffer,
}, },
ObjectSprite, UiSprite, OPENGL_TO_WGPU_MATRIX, ObjectSprite, UiSprite, OPENGL_TO_WGPU_MATRIX,
@ -37,8 +37,9 @@ pub struct GPUState {
window_aspect: f32, window_aspect: f32,
sprite_pipeline: wgpu::RenderPipeline, object_pipeline: wgpu::RenderPipeline,
starfield_pipeline: wgpu::RenderPipeline, starfield_pipeline: wgpu::RenderPipeline,
ui_pipeline: wgpu::RenderPipeline,
starfield: Starfield, starfield: Starfield,
texture_array: TextureArray, texture_array: TextureArray,
@ -47,8 +48,9 @@ pub struct GPUState {
} }
struct VertexBuffers { struct VertexBuffers {
sprite: Rc<VertexBuffer>, object: Rc<VertexBuffer>,
starfield: Rc<VertexBuffer>, starfield: Rc<VertexBuffer>,
ui: Rc<VertexBuffer>,
} }
impl GPUState { impl GPUState {
@ -117,12 +119,12 @@ impl GPUState {
} }
let vertex_buffers = VertexBuffers { let vertex_buffers = VertexBuffers {
sprite: Rc::new(VertexBuffer::new::<TexturedVertex, SpriteInstance>( object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
"sprite", "objecte",
&device, &device,
Some(SPRITE_VERTICES), Some(SPRITE_VERTICES),
Some(SPRITE_INDICES), Some(SPRITE_INDICES),
galactica_constants::SPRITE_INSTANCE_LIMIT, galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT,
)), )),
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>( starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
@ -130,7 +132,15 @@ impl GPUState {
&device, &device,
Some(SPRITE_VERTICES), Some(SPRITE_VERTICES),
Some(SPRITE_INDICES), Some(SPRITE_INDICES),
galactica_constants::STARFIELD_INSTANCE_LIMIT, galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT,
)),
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
"ui",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
galactica_constants::UI_SPRITE_INSTANCE_LIMIT,
)), )),
}; };
@ -145,15 +155,15 @@ impl GPUState {
]; ];
// Create render pipelines // Create render pipelines
let sprite_pipeline = PipelineBuilder::new("sprite", &device) let object_pipeline = PipelineBuilder::new("object", &device)
.set_shader(include_str!(concat!( .set_shader(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
"/shaders/", "/shaders/",
"sprite.wgsl" "object.wgsl"
))) )))
.set_format(config.format) .set_format(config.format)
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(&vertex_buffers.sprite) .set_vertex_buffer(&vertex_buffers.object)
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
@ -169,6 +179,18 @@ impl GPUState {
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
let ui_pipeline = PipelineBuilder::new("ui", &device)
.set_shader(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/",
"ui.wgsl"
)))
.set_format(config.format)
.set_triangle(true)
.set_vertex_buffer(&vertex_buffers.ui)
.set_bind_group_layouts(bind_group_layouts)
.build();
let mut starfield = Starfield::new(); let mut starfield = Starfield::new();
starfield.regenerate(); starfield.regenerate();
@ -182,8 +204,9 @@ impl GPUState {
window_size, window_size,
window_aspect, window_aspect,
sprite_pipeline, object_pipeline,
starfield_pipeline, starfield_pipeline,
ui_pipeline,
starfield, starfield,
texture_array, texture_array,
@ -211,13 +234,13 @@ impl GPUState {
self.update_starfield_buffer() self.update_starfield_buffer()
} }
/// Create a SpriteInstance for an object and add it to `instances`. /// Create a ObjectInstance for an object and add it to `instances`.
/// Also handles child sprites. /// Also handles child sprites.
fn push_object_sprite( fn push_object_sprite(
&self, &self,
camera_zoom: f32, camera_zoom: f32,
camera_pos: Point2<f32>, camera_pos: Point2<f32>,
instances: &mut Vec<SpriteInstance>, instances: &mut Vec<ObjectInstance>,
clip_ne: Point2<f32>, clip_ne: Point2<f32>,
clip_sw: Point2<f32>, clip_sw: Point2<f32>,
s: &ObjectSprite, s: &ObjectSprite,
@ -291,7 +314,7 @@ impl GPUState {
let t = let t =
OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * sprite_aspect_and_scale; OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * sprite_aspect_and_scale;
instances.push(SpriteInstance { instances.push(ObjectInstance {
transform: t.into(), transform: t.into(),
texture_index: texture.index, texture_index: texture.index,
}); });
@ -309,7 +332,7 @@ impl GPUState {
fn push_object_subsprite( fn push_object_subsprite(
&self, &self,
camera_zoom: f32, camera_zoom: f32,
instances: &mut Vec<SpriteInstance>, instances: &mut Vec<ObjectInstance>,
s: &ObjectSubSprite, s: &ObjectSubSprite,
parent_pos: Point2<f32>, parent_pos: Point2<f32>,
parent_angle: Deg<f32>, parent_angle: Deg<f32>,
@ -341,14 +364,14 @@ impl GPUState {
* protate * translate * protate * translate
* rotate * sprite_aspect_and_scale; * rotate * sprite_aspect_and_scale;
instances.push(SpriteInstance { instances.push(ObjectInstance {
transform: t.into(), transform: t.into(),
texture_index: texture.index, texture_index: texture.index,
}); });
} }
/// Create a SpriteInstance for a ui sprite and add it to `instances` /// Create a ObjectInstance for a ui sprite and add it to `instances`
fn push_ui_sprite(&self, instances: &mut Vec<SpriteInstance>, s: &UiSprite) { fn push_ui_sprite(&self, instances: &mut Vec<UiInstance>, s: &UiSprite) {
let logical_size: LogicalSize<f32> = let logical_size: LogicalSize<f32> =
self.window_size.to_logical(self.window.scale_factor()); self.window_size.to_logical(self.window.scale_factor());
@ -394,24 +417,24 @@ impl GPUState {
}); });
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);
instances.push(SpriteInstance { instances.push(UiInstance {
transform: (OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * scale).into(), transform: (OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * scale).into(),
texture_index: texture.index, texture_index: texture.index,
color: s.color.unwrap_or([1.0, 1.0, 1.0, 1.0]),
}); });
} }
/// Make a SpriteInstance for each of the game's visible sprites. /// Make an instance for all the game's sprites
/// Will panic if SPRITE_INSTANCE_LIMIT is exceeded. /// (Objects and UI)
/// /// This will Will panic if any X_SPRITE_INSTANCE_LIMIT is exceeded.
/// This is only called inside self.render() fn update_sprite_instances(
fn make_sprite_instances(
&self, &self,
camera_zoom: f32, camera_zoom: f32,
camera_pos: Point2<f32>, camera_pos: Point2<f32>,
objects: &Vec<ObjectSprite>, objects: &Vec<ObjectSprite>,
ui: &Vec<UiSprite>, ui: &Vec<UiSprite>,
) -> Vec<SpriteInstance> { ) -> (usize, usize) {
let mut instances: Vec<SpriteInstance> = Vec::new(); let mut object_instances: Vec<ObjectInstance> = 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.
@ -419,20 +442,45 @@ impl GPUState {
let clip_sw = Point2::from((self.window_aspect, -1.0)) * camera_zoom; let clip_sw = Point2::from((self.window_aspect, -1.0)) * camera_zoom;
for s in objects { for s in objects {
self.push_object_sprite(camera_zoom, camera_pos, &mut instances, clip_ne, clip_sw, s); self.push_object_sprite(
} camera_zoom,
camera_pos,
for s in ui { &mut object_instances,
self.push_ui_sprite(&mut instances, s); clip_ne,
clip_sw,
s,
);
} }
// Enforce sprite limit // Enforce sprite limit
if instances.len() as u64 > galactica_constants::SPRITE_INSTANCE_LIMIT { if object_instances.len() as u64 > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better. // TODO: no panic, handle this better.
panic!("Sprite limit exceeded!") panic!("Sprite limit exceeded!")
} }
return instances; self.queue.write_buffer(
&self.vertex_buffers.object.instances,
0,
bytemuck::cast_slice(&object_instances),
);
let mut ui_instances: Vec<UiInstance> = Vec::new();
for s in ui {
self.push_ui_sprite(&mut ui_instances, s);
}
if ui_instances.len() as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT {
panic!("Ui sprite limit exceeded!")
}
self.queue.write_buffer(
&self.vertex_buffers.ui.instances,
0,
bytemuck::cast_slice(&ui_instances),
);
return (object_instances.len(), ui_instances.len());
} }
/// Make a StarfieldInstance for each star that needs to be drawn. /// Make a StarfieldInstance for each star that needs to be drawn.
@ -510,13 +558,8 @@ impl GPUState {
); );
// Create sprite instances // Create sprite instances
let sprite_instances = let (n_object, n_ui) =
self.make_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites); self.update_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites);
self.queue.write_buffer(
&self.vertex_buffers.sprite.instances,
0,
bytemuck::cast_slice(&sprite_instances),
);
// These should match the indices in each shader, // These should match the indices in each shader,
// and should each have a corresponding bind group layout. // and should each have a corresponding bind group layout.
@ -533,13 +576,14 @@ impl GPUState {
); );
// Sprite pipeline // Sprite pipeline
self.vertex_buffers.sprite.set_in_pass(&mut render_pass); self.vertex_buffers.object.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.sprite_pipeline); render_pass.set_pipeline(&self.object_pipeline);
render_pass.draw_indexed( render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_object as _);
0..SPRITE_INDICES.len() as u32,
0, // Ui pipeline
0..sprite_instances.len() as _, self.vertex_buffers.ui.set_in_pass(&mut render_pass);
); render_pass.set_pipeline(&self.ui_pipeline);
render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_ui as _);
// begin_render_pass borrows encoder mutably, so we can't call finish() // begin_render_pass borrows encoder mutably, so we can't call finish()
// without dropping this variable. // without dropping this variable.

View File

@ -38,6 +38,10 @@ pub struct UiSprite {
/// This object's position, in logical (dpi-adjusted) pixels /// This object's position, in logical (dpi-adjusted) pixels
pub pos: AnchoredUiPosition, pub pos: AnchoredUiPosition,
/// This sprite's color will be multiplied by this value.
/// If this is None, color will not be changed.
pub color: Option<[f32; 4]>,
/// The size of this sprite, in logical (dpi-adjusted) pixels /// The size of this sprite, in logical (dpi-adjusted) pixels
pub dimensions: Point2<f32>, pub dimensions: Point2<f32>,

View File

@ -94,7 +94,7 @@ impl Starfield {
while ((nw_tile.x * 2 + 1) while ((nw_tile.x * 2 + 1)
* (nw_tile.y * 2 + 1) * (nw_tile.y * 2 + 1)
* galactica_constants::STARFIELD_COUNT as i32) * galactica_constants::STARFIELD_COUNT as i32)
> galactica_constants::STARFIELD_INSTANCE_LIMIT as i32 > galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT as i32
{ {
nw_tile -= Vector2::from((1, 1)); nw_tile -= Vector2::from((1, 1));
} }
@ -119,7 +119,7 @@ impl Starfield {
} }
// Enforce starfield limit // Enforce starfield limit
if instances.len() as u64 > galactica_constants::STARFIELD_INSTANCE_LIMIT { if instances.len() as u64 > galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT {
unreachable!("Starfield limit exceeded!") unreachable!("Starfield limit exceeded!")
} }
self.instance_count = instances.len() as u32; self.instance_count = instances.len() as u32;

View File

@ -81,19 +81,18 @@ impl BufferObject for StarfieldInstance {
} }
} }
// Represents a sprite instance in WGSL
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SpriteInstance { pub struct ObjectInstance {
/// Extra transformations this sprite /// Extra transformations this sprite
/// (rotation, etc) /// (rotation, etc)
pub transform: [[f32; 4]; 4], pub transform: [[f32; 4]; 4],
// What texture to use for this sprite /// What texture to use for this sprite
pub texture_index: u32, pub texture_index: u32,
} }
impl BufferObject for SpriteInstance { impl BufferObject for ObjectInstance {
fn layout() -> wgpu::VertexBufferLayout<'static> { fn layout() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
array_stride: Self::SIZE, array_stride: Self::SIZE,
@ -133,3 +132,66 @@ impl BufferObject for SpriteInstance {
} }
} }
} }
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct UiInstance {
/// Extra transformations this sprite
/// (rotation, etc)
pub transform: [[f32; 4]; 4],
/// This lets us color ui sprites dynamically.
/// Each fragment's color is multiplied by this value.
/// Fill this array with ones if no recoloring should be done.
pub color: [f32; 4],
/// What texture to use for this sprite
pub texture_index: u32,
}
impl BufferObject for UiInstance {
fn layout() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: Self::SIZE,
// Switch to a step mode of Vertex to Instance.
// This means that our shaders will only change to use the next
// instance when the shader starts processing a new instance
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[
// 4 arrays = 1 4x4 matrix
wgpu::VertexAttribute {
offset: 0,
shader_location: 2,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
shader_location: 3,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
shader_location: 4,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
shader_location: 5,
format: wgpu::VertexFormat::Float32x4,
},
// Color
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress,
shader_location: 6,
format: wgpu::VertexFormat::Float32x4,
},
// Texture
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 20]>() as wgpu::BufferAddress,
shader_location: 7,
format: wgpu::VertexFormat::Uint32,
},
],
}
}
}

View File

@ -24,6 +24,7 @@ pub fn build_radar(
y: radar_size, y: radar_size,
}, },
angle: Deg(0.0), angle: Deg(0.0),
color: None,
}); });
// Draw viewport frame // Draw viewport frame
@ -33,7 +34,7 @@ pub fn build_radar(
}; };
let m = d.magnitude() / radar_range; let m = d.magnitude() / radar_range;
let d = d / radar_range * (radar_size / 2.0); let d = d / radar_range * (radar_size / 2.0);
println!("{:?}", d); let color = Some([0.5, 0.5, 0.5, 1.0]);
if m < 0.8 { if m < 0.8 {
let texture = ct.get_texture_handle("ui::radarframe"); let texture = ct.get_texture_handle("ui::radarframe");
let dimensions = Point2 { let dimensions = Point2 {
@ -48,6 +49,7 @@ pub fn build_radar(
}), }),
dimensions, dimensions,
angle: Deg(0.0), angle: Deg(0.0),
color,
}); });
out.push(UiSprite { out.push(UiSprite {
@ -58,6 +60,7 @@ pub fn build_radar(
}), }),
dimensions, dimensions,
angle: Deg(90.0), angle: Deg(90.0),
color,
}); });
out.push(UiSprite { out.push(UiSprite {
@ -68,6 +71,7 @@ pub fn build_radar(
}), }),
dimensions, dimensions,
angle: Deg(180.0), angle: Deg(180.0),
color,
}); });
out.push(UiSprite { out.push(UiSprite {
@ -78,6 +82,7 @@ pub fn build_radar(
}), }),
dimensions, dimensions,
angle: Deg(270.0), angle: Deg(270.0),
color,
}); });
} }
@ -88,23 +93,13 @@ pub fn build_radar(
let p = util::rigidbody_position(r); let p = util::rigidbody_position(r);
let d = p - pr; let d = p - pr;
let m = d.magnitude() / radar_range; let m = d.magnitude() / radar_range;
let angle: Deg<f32> = util::rigidbody_rotation(r) if m < 0.8 {
.angle(Vector2 { x: 0.0, y: 1.0 }) let angle: Deg<f32> = util::rigidbody_rotation(r)
.into(); .angle(Vector2 { x: 0.0, y: 1.0 })
if s.physics_handle == *player { .into();
out.push(UiSprite { let f = ct.get_faction(s.ship.faction).color;
texture, let f = [f[0], f[1], f[2], 1.0];
pos: AnchoredUiPosition::NwC(Point2 {
x: radar_size / 2.0 + 10.0,
y: radar_size / -2.0 - 10.0,
}),
dimensions: Point2 {
x: texture.aspect,
y: 1.0,
} * 5.0f32.min((0.8 - m) * 50.0),
angle: -angle,
});
} else if m < 0.8 {
out.push(UiSprite { out.push(UiSprite {
texture, texture,
pos: AnchoredUiPosition::NwC( pos: AnchoredUiPosition::NwC(
@ -118,6 +113,7 @@ pub fn build_radar(
y: 1.0, y: 1.0,
} * 5.0f32.min((0.8 - m) * 50.0), } * 5.0f32.min((0.8 - m) * 50.0),
angle: -angle, angle: -angle,
color: Some(f),
}); });
} }
} }

View File

@ -49,6 +49,9 @@ impl Game {
let s = object::Ship::new( let s = object::Ship::new(
&ct, &ct,
content::ShipHandle { index: 0 }, content::ShipHandle { index: 0 },
// This method of specifying factions is non-deterministic,
// but that's ok since this is for debug.
// TODO: fix
content::FactionHandle { index: 0 }, content::FactionHandle { index: 0 },
object::OutfitSet::new(ss), object::OutfitSet::new(ss),
); );