Compare commits
6 Commits
c7077fc701
...
1992bd7bb8
Author | SHA1 | Date |
---|---|---|
Mark | 1992bd7bb8 | |
Mark | c1d50f0c45 | |
Mark | a65372f866 | |
Mark | b28cb03a52 | |
Mark | 7eaa585ede | |
Mark | 39020efd6c |
13
TODO.md
13
TODO.md
|
@ -1,6 +1,5 @@
|
||||||
## Specific Jobs
|
## Specific Jobs
|
||||||
- Particle variation
|
- Particle variation
|
||||||
- Particle physics
|
|
||||||
- UI: health, shield, fuel, heat, energy bars
|
- UI: health, shield, fuel, heat, energy bars
|
||||||
- UI: text arranger
|
- UI: text arranger
|
||||||
- Sound system
|
- Sound system
|
||||||
|
@ -9,12 +8,10 @@
|
||||||
- Passive engine glow
|
- Passive engine glow
|
||||||
- Ship death damage and force events
|
- Ship death damage and force events
|
||||||
- Fix particle inherit velocity
|
- Fix particle inherit velocity
|
||||||
- Global shader variable prefix
|
|
||||||
|
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
## Game & Story
|
## Game & Story
|
||||||
- Landmarks to determine speed?
|
|
||||||
- How to keep player in system bounds, what to do if they fly far away
|
- How to keep player in system bounds, what to do if they fly far away
|
||||||
- Max chase distance, physics-enforced area?
|
- Max chase distance, physics-enforced area?
|
||||||
- (Soft limits, you shouldn't go too far unless you decide to.)
|
- (Soft limits, you shouldn't go too far unless you decide to.)
|
||||||
|
@ -62,11 +59,11 @@
|
||||||
- how to target them
|
- how to target them
|
||||||
- where to go
|
- where to go
|
||||||
- etc, extra flags
|
- etc, extra flags
|
||||||
- Conditional animations: on fire, on death, etc (tempest)
|
|
||||||
- Higher texture limit (16 x 8096 x 8096 isn't enough)
|
- Higher texture limit (16 x 8096 x 8096 isn't enough)
|
||||||
- Fast-load menu, progress bar for the rest
|
- Fast-load menu, progress bar for the rest
|
||||||
- Only load what is needed?
|
- Only load what is needed?
|
||||||
- GPU limits? (texture size, texture number)
|
- GPU limits? (texture size, texture number)
|
||||||
|
- Advanced particle physics (must move to cpu. Maybe both?)
|
||||||
|
|
||||||
|
|
||||||
## Faction interaction
|
## Faction interaction
|
||||||
|
@ -74,7 +71,6 @@
|
||||||
- Static and dynamic faction relationships (change with player actions/game events?)
|
- Static and dynamic faction relationships (change with player actions/game events?)
|
||||||
- Dynamic relationships only for player? Other governments may be hard-coded
|
- Dynamic relationships only for player? Other governments may be hard-coded
|
||||||
- Opinion towards player -> how to handle well?
|
- Opinion towards player -> how to handle well?
|
||||||
- Player faction is special
|
|
||||||
- Actions against one faction affect another
|
- Actions against one faction affect another
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,9 +101,6 @@
|
||||||
- Weapons with ammunition
|
- Weapons with ammunition
|
||||||
- Enable/disable weapons
|
- Enable/disable weapons
|
||||||
- Cargo space
|
- Cargo space
|
||||||
- List of ES content--which to implement?
|
|
||||||
- Ship stats
|
|
||||||
- Weapon stats
|
|
||||||
- Display name != internal key
|
- Display name != internal key
|
||||||
- Conversations
|
- Conversations
|
||||||
- Trade
|
- Trade
|
||||||
|
@ -158,10 +151,10 @@
|
||||||
- More interesting missions
|
- More interesting missions
|
||||||
- Big fleets shouldn't be broken (should the player even have a fleet?)
|
- Big fleets shouldn't be broken (should the player even have a fleet?)
|
||||||
- ES Enemies don't have a real motivation.
|
- ES Enemies don't have a real motivation.
|
||||||
- ES story is a bit basic
|
|
||||||
- Death matters - save-scumming shouldn't be normal
|
- Death matters - save-scumming shouldn't be normal
|
||||||
- More interesting trading?
|
- More interesting trading?
|
||||||
- Death penalty
|
- Death penalty
|
||||||
- Find your wreckage when you die (dark souls/HK)
|
- Find your wreckage when you die (dark souls/HK)
|
||||||
- Lose some outfits, lose ship? Real risk for going out! (HK does this well)
|
- Lose some outfits, lose ship? Real risk for going out! (HK does this well)
|
||||||
- Damage to ship subsystems
|
- Damage to ship subsystems
|
||||||
|
- Repair your own ship
|
|
@ -3,32 +3,61 @@ sprite = "particle::explosion::small"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "target"
|
||||||
size = 8.0
|
size = 8.0
|
||||||
|
size_rng = 1.6
|
||||||
|
angle_rng = 360
|
||||||
|
velocity_scale_parent = 1.0
|
||||||
|
|
||||||
|
|
||||||
[effect."large explosion"]
|
[effect."large explosion"]
|
||||||
sprite = "particle::explosion::large"
|
sprite = "particle::explosion::large"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "target"
|
||||||
size = 25.0
|
size = 25.0
|
||||||
|
size_rng = 5.0
|
||||||
|
angle_rng = 360
|
||||||
|
velocity_scale_parent = 1.0
|
||||||
|
|
||||||
|
|
||||||
[effect."huge explosion"]
|
[effect."huge explosion"]
|
||||||
sprite = "particle::explosion::huge"
|
sprite = "particle::explosion::huge"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "target"
|
||||||
size = 50.0
|
size = 50.0
|
||||||
|
size_rng = 10.0
|
||||||
|
angle_rng = 360
|
||||||
|
velocity_scale_parent = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
# Every effect has a parent, some effects have a target
|
||||||
[effect."blaster impact"]
|
[effect."blaster impact"]
|
||||||
sprite = "particle::blaster"
|
sprite = "particle::blaster"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit" # number in seconds or inherit from sprite
|
||||||
inherit_velocity = "target"
|
lifetime_rng = 0.0 # Random variation of lifetime (up to this value)
|
||||||
size = 3.0
|
|
||||||
|
size = 3.0 # sprite size, in game units
|
||||||
|
size_rng = 1.0 # random size variation
|
||||||
|
|
||||||
|
angle = 0.0 # absolute starting angle. always added to parent angle.
|
||||||
|
angle_rng = 90.0 # Starting angle randomness (up to this value)
|
||||||
|
|
||||||
|
# Does not affect velocity, only sprite angle
|
||||||
|
angvel_rng = 0.0 # Angvel randomness, applied to angvel
|
||||||
|
angvel = 0.0 # Angular velocity at creation
|
||||||
|
|
||||||
|
|
||||||
|
# Total velocity is sum of parent + target velocities with scale applied
|
||||||
|
velocity_scale_parent = 0.0 # Multiply velocity by this value
|
||||||
|
velocity_scale_parent_rng = 0.0 # random variation of scale
|
||||||
|
velocity_scale_target = 1.0
|
||||||
|
velocity_scale_target_rng = 1.0
|
||||||
|
|
||||||
|
direction_rng = 1.0 # Random variation of travel direction, in degrees, applied to velocity vector (/2 each side?)
|
||||||
|
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# inherit velocity scale
|
|
||||||
# absolute velocity/angle (no inherit)
|
|
||||||
# random lifetime, velocity, angle, spin
|
|
||||||
# bullet bounce effect: inherit and change velocity
|
|
||||||
# effect probabilities & variants
|
# effect probabilities & variants
|
||||||
# multiple particles in one effect
|
# multiple particles in one effect
|
||||||
# fade
|
# fade
|
||||||
# document: effect vs particle
|
# document: effect vs particle
|
||||||
|
# sprite lifetime/fps variation (and effects inherit lifetime later)
|
||||||
|
# universal effect creator
|
||||||
|
|
|
@ -33,5 +33,5 @@ projectile.impact_effect = "blaster impact"
|
||||||
|
|
||||||
projectile.expire_effect.sprite = "particle::blaster"
|
projectile.expire_effect.sprite = "particle::blaster"
|
||||||
projectile.expire_effect.lifetime = "inherit"
|
projectile.expire_effect.lifetime = "inherit"
|
||||||
projectile.expire_effect.inherit_velocity = "projectile"
|
|
||||||
projectile.expire_effect.size = 3.0
|
projectile.expire_effect.size = 3.0
|
||||||
|
projectile.expire_effect.velocity_scale_parent = 1.0
|
||||||
|
|
|
@ -57,7 +57,7 @@ collision = [
|
||||||
|
|
||||||
# Scripted explosion
|
# Scripted explosion
|
||||||
[[ship."Gypsum".collapse.event]]
|
[[ship."Gypsum".collapse.event]]
|
||||||
time = 5.0
|
time = 4.9
|
||||||
effects = [
|
effects = [
|
||||||
#[rustfmt:skip],
|
#[rustfmt:skip],
|
||||||
{ effect = "small explosion", count = 8 },
|
{ effect = "small explosion", count = 8 },
|
||||||
|
|
|
@ -26,7 +26,7 @@ file = "projectile/blaster.png"
|
||||||
file = "ship/gypsum.png"
|
file = "ship/gypsum.png"
|
||||||
|
|
||||||
[sprite."ship::peregrine"]
|
[sprite."ship::peregrine"]
|
||||||
timing.duration = 1.3
|
timing.duration = 2
|
||||||
repeat = "reverse"
|
repeat = "reverse"
|
||||||
random_start_frame = true
|
random_start_frame = true
|
||||||
frames = [
|
frames = [
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use serde::Deserialize;
|
use cgmath::Rad;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle};
|
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle};
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use cgmath::Deg;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{Content, ContentBuildContext, EffectHandle};
|
use crate::{Content, ContentBuildContext, EffectHandle};
|
||||||
|
@ -15,9 +16,26 @@ pub(crate) mod syntax {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Effect {
|
pub struct Effect {
|
||||||
pub sprite: String,
|
pub sprite: String,
|
||||||
pub lifetime: EffectLifetime,
|
|
||||||
pub inherit_velocity: super::ImpactInheritVelocity,
|
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
|
pub size_rng: Option<f32>,
|
||||||
|
pub lifetime: TextOrFloat,
|
||||||
|
pub lifetime_rng: Option<f32>,
|
||||||
|
pub angle: Option<f32>,
|
||||||
|
pub angle_rng: Option<f32>,
|
||||||
|
pub angvel: Option<f32>,
|
||||||
|
pub angvel_rng: Option<f32>,
|
||||||
|
pub velocity_scale_parent: Option<f32>,
|
||||||
|
pub velocity_scale_parent_rng: Option<f32>,
|
||||||
|
pub velocity_scale_target: Option<f32>,
|
||||||
|
pub velocity_scale_target_rng: Option<f32>,
|
||||||
|
pub direction_rng: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum TextOrFloat {
|
||||||
|
Text(String),
|
||||||
|
Float(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We implement building here instead of in super::Effect because
|
// We implement building here instead of in super::Effect because
|
||||||
|
@ -34,8 +52,8 @@ pub(crate) mod syntax {
|
||||||
};
|
};
|
||||||
|
|
||||||
let lifetime = match self.lifetime {
|
let lifetime = match self.lifetime {
|
||||||
EffectLifetime::Seconds(s) => s,
|
TextOrFloat::Float(f) => f,
|
||||||
EffectLifetime::Inherit(s) => {
|
TextOrFloat::Text(s) => {
|
||||||
if s == "inherit" {
|
if s == "inherit" {
|
||||||
let sprite = content.get_sprite(sprite);
|
let sprite = content.get_sprite(sprite);
|
||||||
sprite.fps * sprite.frames.len() as f32
|
sprite.fps * sprite.frames.len() as f32
|
||||||
|
@ -49,24 +67,27 @@ pub(crate) mod syntax {
|
||||||
index: content.effects.len(),
|
index: content.effects.len(),
|
||||||
};
|
};
|
||||||
content.effects.push(super::Effect {
|
content.effects.push(super::Effect {
|
||||||
sprite,
|
|
||||||
lifetime,
|
|
||||||
handle,
|
handle,
|
||||||
inherit_velocity: self.inherit_velocity,
|
sprite,
|
||||||
size: self.size,
|
size: self.size,
|
||||||
|
size_rng: self.size_rng.unwrap_or(0.0),
|
||||||
|
lifetime,
|
||||||
|
lifetime_rng: self.lifetime_rng.unwrap_or(0.0),
|
||||||
|
angle: Deg(self.angle.unwrap_or(0.0) / 2.0).into(),
|
||||||
|
angle_rng: Deg(self.angle_rng.unwrap_or(0.0) / 2.0).into(),
|
||||||
|
angvel: Deg(self.angvel.unwrap_or(0.0)).into(),
|
||||||
|
angvel_rng: Deg(self.angle_rng.unwrap_or(0.0)).into(),
|
||||||
|
velocity_scale_parent: self.velocity_scale_parent.unwrap_or(0.0),
|
||||||
|
velocity_scale_parent_rng: self.velocity_scale_parent_rng.unwrap_or(0.0),
|
||||||
|
velocity_scale_target: self.velocity_scale_target.unwrap_or(0.0),
|
||||||
|
velocity_scale_target_rng: self.velocity_scale_target_rng.unwrap_or(0.0),
|
||||||
|
direction_rng: Deg(self.direction_rng.unwrap_or(0.0) / 2.0).into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok(handle);
|
return Ok(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum EffectLifetime {
|
|
||||||
Inherit(String),
|
|
||||||
Seconds(f32),
|
|
||||||
}
|
|
||||||
|
|
||||||
// This isn't used here, but is pulled in by other content items.
|
// This isn't used here, but is pulled in by other content items.
|
||||||
/// A reference to an effect by name, or an inline definition.
|
/// A reference to an effect by name, or an inline definition.
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -95,43 +116,54 @@ pub(crate) mod syntax {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How we should set an effect's velocity
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub enum ImpactInheritVelocity {
|
|
||||||
/// Don't inherit any velocity.
|
|
||||||
/// This impact particle will be still.
|
|
||||||
#[serde(rename = "don't")]
|
|
||||||
Dont,
|
|
||||||
|
|
||||||
/// Inherit target velocity.
|
|
||||||
/// This impact particle will stick to the object it hits.
|
|
||||||
#[serde(rename = "target")]
|
|
||||||
Target,
|
|
||||||
|
|
||||||
/// Inherit projectile velocity.
|
|
||||||
/// This impact particle will continue on its projectile's path.
|
|
||||||
#[serde(rename = "projectile")]
|
|
||||||
Projectile,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The particle a projectile will spawn when it hits something
|
/// The particle a projectile will spawn when it hits something
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Effect {
|
pub struct Effect {
|
||||||
/// The sprite to use for this particle.
|
|
||||||
/// This is most likely animated.
|
|
||||||
pub sprite: SpriteHandle,
|
|
||||||
|
|
||||||
/// This effect's handle
|
/// This effect's handle
|
||||||
pub handle: EffectHandle,
|
pub handle: EffectHandle,
|
||||||
|
|
||||||
|
/// The sprite to use for this particle.
|
||||||
|
pub sprite: SpriteHandle,
|
||||||
|
|
||||||
|
/// The height of this particle, in game units.
|
||||||
|
pub size: f32,
|
||||||
|
|
||||||
|
/// Random size variation
|
||||||
|
pub size_rng: f32,
|
||||||
|
|
||||||
/// How many seconds this particle should live
|
/// How many seconds this particle should live
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
|
|
||||||
/// How we should set this particle's velocity
|
/// Random lifetime variation
|
||||||
pub inherit_velocity: ImpactInheritVelocity,
|
pub lifetime_rng: f32,
|
||||||
|
|
||||||
/// The height of this particle, in game units.
|
/// The angle this particle points once spawned
|
||||||
pub size: f32,
|
pub angle: Rad<f32>,
|
||||||
|
|
||||||
|
/// Random angle variation
|
||||||
|
pub angle_rng: Rad<f32>,
|
||||||
|
|
||||||
|
/// How fast this particle spins
|
||||||
|
pub angvel: Rad<f32>,
|
||||||
|
|
||||||
|
/// Random angvel variation
|
||||||
|
pub angvel_rng: Rad<f32>,
|
||||||
|
|
||||||
|
/// The amount of this particle's parent's velocity to inherit
|
||||||
|
pub velocity_scale_parent: f32,
|
||||||
|
|
||||||
|
/// Parent velocity random variation
|
||||||
|
pub velocity_scale_parent_rng: f32,
|
||||||
|
|
||||||
|
/// The amount of this particle's parent's target velocity to inherit.
|
||||||
|
/// If there is no target, this is zero.
|
||||||
|
pub velocity_scale_target: f32,
|
||||||
|
|
||||||
|
/// Target velocity random variation
|
||||||
|
pub velocity_scale_target_rng: f32,
|
||||||
|
|
||||||
|
/// Travel direction random variation
|
||||||
|
pub direction_rng: Rad<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Build for Effect {
|
impl crate::Build for Effect {
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub(crate) mod ship;
|
||||||
pub(crate) mod sprite;
|
pub(crate) mod sprite;
|
||||||
pub(crate) mod system;
|
pub(crate) mod system;
|
||||||
|
|
||||||
pub use effect::{Effect, ImpactInheritVelocity};
|
pub use effect::Effect;
|
||||||
pub use faction::{Faction, Relationship};
|
pub use faction::{Faction, Relationship};
|
||||||
pub use gun::{Gun, Projectile, ProjectileCollider};
|
pub use gun::{Gun, Projectile, ProjectileCollider};
|
||||||
pub use outfit::Outfit;
|
pub use outfit::Outfit;
|
||||||
|
|
|
@ -1,43 +1,41 @@
|
||||||
// Pick frame of animation from an instance.
|
// Pick a frame of a sprite animation from an instance.
|
||||||
//
|
fn animate(sprite_index: u32, age: f32, offset: f32) -> f32 {
|
||||||
// This function assumes that the uniform header has been loaded,
|
|
||||||
// and that `InstanceInput` contains a field `texture_index`
|
|
||||||
fn animate(instance: InstanceInput, age: f32) -> u32 {
|
|
||||||
|
|
||||||
let idx = instance.texture_index;
|
|
||||||
let len = sprites[idx].frame_count;
|
|
||||||
let rep = sprites[idx].repeatmode;
|
|
||||||
let fps = sprites[idx].fps;
|
|
||||||
var frame: u32 = u32(0);
|
|
||||||
|
|
||||||
|
let len = global_sprites[sprite_index].frame_count;
|
||||||
|
let rep = global_sprites[sprite_index].repeatmode;
|
||||||
|
let fps = global_sprites[sprite_index].fps;
|
||||||
|
var frame: f32 = 0.0;
|
||||||
|
|
||||||
// Once
|
// Once
|
||||||
if rep == u32(1) {
|
if rep == u32(1) {
|
||||||
|
|
||||||
frame = u32(min(
|
frame = min(
|
||||||
age / fps,
|
age / fps + offset,
|
||||||
f32(len) - 1.0
|
f32(len) - 1.0
|
||||||
));
|
);
|
||||||
|
|
||||||
// Reverse
|
// Reverse
|
||||||
} else if rep == u32(2) {
|
} else if rep == u32(2) {
|
||||||
let x = age / fps;
|
let x = age / fps + offset;
|
||||||
let m = f32(len) * 2.0 - 1.0;
|
let m = f32(len) * 2.0 - 1.0;
|
||||||
// x fmod m
|
// x fmod m
|
||||||
frame = u32(x - floor(x / m) * m);
|
frame = x - floor(x / m) * m;
|
||||||
|
|
||||||
if frame >= len {
|
if frame >= f32(len) {
|
||||||
frame = len + len - frame - u32(1);
|
frame = (
|
||||||
|
f32(len) + f32(len) - 1.0
|
||||||
|
// Split integer and fractional part so tweening works properly
|
||||||
|
- floor(frame)
|
||||||
|
+ fract(frame)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat (default)
|
// Repeat (default)
|
||||||
} else {
|
} else {
|
||||||
let x = age / fps;
|
let x = age / fps + offset;
|
||||||
let m = f32(len);
|
let m = f32(len);
|
||||||
// x fmod m
|
frame = x - floor(x / m) * m;
|
||||||
frame = u32(x - floor(x / m) * m);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame + sprites[idx].first_frame;
|
return frame + f32(global_sprites[sprite_index].first_frame);
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
// INCLUDE: global uniform header
|
// INCLUDE: global uniform header
|
||||||
|
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) texture_index: u32,
|
@location(2) sprite_index: u32,
|
||||||
@location(3) object_index: u32,
|
@location(3) object_index: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,8 +12,11 @@ struct VertexInput {
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position) position: vec4<f32>,
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) texture_coords: vec2<f32>,
|
@location(0) tween: f32,
|
||||||
@location(1) texture_index: u32,
|
@location(1) texture_index_a: u32,
|
||||||
|
@location(2) texture_coords_a: vec2<f32>,
|
||||||
|
@location(3) texture_index_b: u32,
|
||||||
|
@location(4) texture_coords_b: vec2<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,42 +28,76 @@ var sampler_array: binding_array<sampler>;
|
||||||
|
|
||||||
// INCLUDE: animate.wgsl
|
// INCLUDE: animate.wgsl
|
||||||
|
|
||||||
fn transform_vertex(obj: ObjectLocation, vertex: VertexInput, instance: InstanceInput) -> vec4<f32> {
|
fn transform_vertex(obj: ObjectData, vertex_position: vec2<f32>, sprite_index: u32) -> vec4<f32> {
|
||||||
// Object scale
|
// Object scale
|
||||||
let scale = obj.size / (global.camera_zoom.x * obj.zpos);
|
let scale = obj.size / (global_data.camera_zoom.x * obj.zpos);
|
||||||
|
|
||||||
// Apply scale and sprite aspect
|
// Apply scale and sprite aspect
|
||||||
// Note that our mesh starts centered at (0, 0). This is important!
|
// Note that our mesh starts centered at (0, 0). This is important!
|
||||||
var pos: vec2<f32> = vec2(
|
var pos: vec2<f32> = vec2(
|
||||||
vertex.position.x * scale * sprites[instance.texture_index].aspect,
|
vertex_position.x * scale * global_sprites[sprite_index].aspect,
|
||||||
vertex.position.y * scale
|
vertex_position.y * scale
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply rotation
|
// Apply rotation
|
||||||
pos = mat2x2(
|
pos = mat2x2(
|
||||||
vec2(cos(obj.angle), sin(obj.angle)),
|
vec2(cos(obj.angle), sin(obj.angle)),
|
||||||
vec2(-sin(obj.angle), cos(obj.angle))
|
vec2(-sin(obj.angle), cos(obj.angle))
|
||||||
) * pos;
|
) * pos;
|
||||||
|
|
||||||
|
|
||||||
// Correct for screen aspect, preserving height
|
// Correct for screen aspect, preserving height
|
||||||
// This must be done AFTER rotation.
|
// This must be done AFTER rotation.
|
||||||
pos = vec2(
|
// (It's thus done later if this is a child)
|
||||||
pos.x / global.window_aspect.x,
|
if obj.is_child == u32(0) {
|
||||||
pos.y
|
pos = vec2(
|
||||||
);
|
pos.x / global_data.window_aspect.x,
|
||||||
|
pos.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Distance-adjusted world position
|
// Translate
|
||||||
let trans = (vec2(obj.xpos, obj.ypos) - global.camera_position) / obj.zpos;
|
|
||||||
|
|
||||||
// Finally, translate
|
|
||||||
//
|
//
|
||||||
// Note that we divide camera zoom by two.
|
// Note that we divide camera zoom by two.
|
||||||
// The height of the viewport is `zoom` in game units,
|
// The height of the viewport is `zoom` in game units,
|
||||||
// but it's 2 in screen units (since coordinates range from -1 to 1)
|
// but it's 2 in screen units (since coordinates range from -1 to 1)
|
||||||
pos = pos + vec2(
|
if obj.is_child == u32(0) {
|
||||||
trans.x / (global.camera_zoom.x / 2.0) / global.window_aspect.x,
|
let trans = (vec2(obj.xpos, obj.ypos) - global_data.camera_position) / obj.zpos;
|
||||||
trans.y / (global.camera_zoom.x / 2.0)
|
pos = pos + vec2(
|
||||||
);
|
trans.x / (global_data.camera_zoom.x / 2.0) / global_data.window_aspect.x,
|
||||||
|
trans.y / (global_data.camera_zoom.x / 2.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.is_child == u32(1) {
|
||||||
|
// Apply translation relative to parent
|
||||||
|
// Note that obj.zpos is ignored
|
||||||
|
pos = pos + vec2(
|
||||||
|
obj.xpos / (global_data.camera_zoom.x / 2.0) / global_data.window_aspect.x,
|
||||||
|
obj.ypos / (global_data.camera_zoom.x / 2.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
let parent = objects[obj.parent];
|
||||||
|
|
||||||
|
// Apply parent's rotation
|
||||||
|
pos = mat2x2(
|
||||||
|
vec2(cos(parent.angle), sin(parent.angle)),
|
||||||
|
vec2(-sin(parent.angle), cos(parent.angle))
|
||||||
|
) * pos;
|
||||||
|
|
||||||
|
// Correct for screen aspect, preserving height
|
||||||
|
pos = vec2(
|
||||||
|
pos.x / global_data.window_aspect.x,
|
||||||
|
pos.y
|
||||||
|
);
|
||||||
|
|
||||||
|
// Apply parent's translation
|
||||||
|
let ptrans = (vec2(parent.xpos, parent.ypos) - global_data.camera_position) / parent.zpos;
|
||||||
|
pos = pos + vec2(
|
||||||
|
ptrans.x / (global_data.camera_zoom.x / 2.0) / global_data.window_aspect.x,
|
||||||
|
ptrans.y / (global_data.camera_zoom.x / 2.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return vec4<f32>(pos, 0.0, 1.0);;
|
return vec4<f32>(pos, 0.0, 1.0);;
|
||||||
}
|
}
|
||||||
|
@ -71,16 +108,36 @@ fn vertex_main(
|
||||||
instance: InstanceInput,
|
instance: InstanceInput,
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.position = transform_vertex(objects[instance.object_index], vertex, instance);
|
|
||||||
|
|
||||||
let t = atlas[animate(instance, global.current_time.x)];
|
|
||||||
out.texture_index = t.atlas_texture;
|
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
out.position = transform_vertex(objects[instance.object_index], vertex.position.xy, instance.sprite_index);
|
||||||
|
|
||||||
|
|
||||||
|
// Compute texture coordinates
|
||||||
|
|
||||||
|
let age = global_data.current_time.x;
|
||||||
|
let frame = animate(instance.sprite_index, age, 0.0);
|
||||||
|
out.tween = fract(frame);
|
||||||
|
|
||||||
|
let t = global_atlas[u32(floor(frame))];
|
||||||
|
out.texture_index_a = u32(t.atlas_texture);
|
||||||
|
out.texture_coords_a = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
out.texture_coords_a = out.texture_coords_a + vec2(t.width, 0.0);
|
||||||
}
|
}
|
||||||
if vertex.texture_coords.y == 1.0 {
|
if vertex.texture_coords.y == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
out.texture_coords_a = out.texture_coords_a + vec2(0.0, t.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = global_atlas[u32(floor(animate(instance.sprite_index, age, 1.0)))];
|
||||||
|
out.texture_index_b = u32(b.atlas_texture);
|
||||||
|
out.texture_coords_b = vec2(b.xpos, b.ypos);
|
||||||
|
if vertex.texture_coords.x == 1.0 {
|
||||||
|
out.texture_coords_b = out.texture_coords_b + vec2(b.width, 0.0);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords_b = out.texture_coords_b + vec2(0.0, b.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -89,10 +146,19 @@ fn vertex_main(
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
return textureSampleLevel(
|
return mix(
|
||||||
texture_array[in.texture_index],
|
textureSampleLevel(
|
||||||
sampler_array[0],
|
texture_array[in.texture_index_a],
|
||||||
in.texture_coords,
|
sampler_array[0],
|
||||||
0.0
|
in.texture_coords_a,
|
||||||
).rgba;
|
0.0
|
||||||
|
).rgba,
|
||||||
|
textureSampleLevel(
|
||||||
|
texture_array[in.texture_index_b],
|
||||||
|
sampler_array[0],
|
||||||
|
in.texture_coords_b,
|
||||||
|
0.0
|
||||||
|
).rgba,
|
||||||
|
in.tween
|
||||||
|
);
|
||||||
}
|
}
|
|
@ -3,12 +3,12 @@
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) position: vec2<f32>,
|
@location(2) position: vec2<f32>,
|
||||||
@location(3) velocity: vec2<f32>,
|
@location(3) velocity: vec2<f32>,
|
||||||
@location(4) rotation_0: vec2<f32>,
|
@location(4) angle: f32,
|
||||||
@location(5) rotation_1: vec2<f32>,
|
@location(5) angvel: f32,
|
||||||
@location(6) size: f32,
|
@location(6) size: f32,
|
||||||
@location(7) created: f32,
|
@location(7) created: f32,
|
||||||
@location(8) expires: f32,
|
@location(8) expires: f32,
|
||||||
@location(9) texture_index: u32,
|
@location(9) sprite_index: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
|
@ -18,8 +18,11 @@ struct VertexInput {
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position) position: vec4<f32>,
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) texture_coords: vec2<f32>,
|
@location(0) tween: f32,
|
||||||
@location(1) texture_index: u32,
|
@location(1) texture_index_a: u32,
|
||||||
|
@location(2) texture_coords_a: vec2<f32>,
|
||||||
|
@location(3) texture_index_b: u32,
|
||||||
|
@location(4) texture_coords_b: vec2<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,64 +41,95 @@ fn vertex_main(
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
|
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.texture_coords = vertex.texture_coords;
|
|
||||||
|
|
||||||
// Skip expired particles
|
// Skip expired particles
|
||||||
if instance.expires < global.current_time.x {
|
if instance.expires < global_data.current_time.x {
|
||||||
out.texture_index = u32(0);
|
out.tween = 0.0;
|
||||||
// Draw off screen
|
out.texture_index_a = u32(0);
|
||||||
|
out.texture_index_b = u32(0);
|
||||||
|
out.texture_coords_a = vec2(0.0, 0.0);
|
||||||
|
out.texture_coords_b = vec2(0.0, 0.0);
|
||||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
let age = global.current_time.x - instance.created;
|
let age = global_data.current_time.x - instance.created;
|
||||||
|
|
||||||
|
|
||||||
// Apply transformations
|
// Apply transformations
|
||||||
let rotation = mat2x2(instance.rotation_0, instance.rotation_1);
|
let angle = instance.angle + age * instance.angvel;
|
||||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
|
||||||
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
|
||||||
pos = pos * vec2<f32>(
|
|
||||||
sprites[instance.texture_index].aspect * scale / global.window_aspect.x,
|
|
||||||
scale
|
|
||||||
);
|
|
||||||
|
|
||||||
pos = rotation * pos;
|
|
||||||
|
|
||||||
var ipos: vec2<f32> = (
|
var scale: f32 = instance.size / global_data.camera_zoom.x;
|
||||||
vec2(instance.position.x, instance.position.y)
|
var pos: vec2<f32> = vec2(
|
||||||
+ (instance.velocity * age)
|
vertex.position.x * scale * global_sprites[instance.sprite_index].aspect,
|
||||||
- global.camera_position
|
vertex.position.y * scale
|
||||||
|
);
|
||||||
|
pos = mat2x2(
|
||||||
|
vec2(cos(angle), sin(angle)),
|
||||||
|
vec2(-sin(angle), cos(angle))
|
||||||
|
) * pos;
|
||||||
|
|
||||||
|
var trans: vec2<f32> = (instance.position + (instance.velocity * age) - global_data.camera_position);
|
||||||
|
|
||||||
|
pos = vec2(
|
||||||
|
pos.x / global_data.window_aspect.x,
|
||||||
|
pos.y
|
||||||
);
|
);
|
||||||
|
|
||||||
pos = pos + vec2<f32>(
|
pos = pos + vec2(
|
||||||
ipos.x / (global.camera_zoom.x/2.0) / global.window_aspect.x,
|
trans.x / (global_data.camera_zoom.x / 2.0) / global_data.window_aspect.x,
|
||||||
ipos.y / (global.camera_zoom.x/2.0)
|
trans.y / (global_data.camera_zoom.x / 2.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
out.position = vec4<f32>(pos, 0.0, 1.0);
|
|
||||||
|
out.position = vec4(pos, 0.0, 1.0);
|
||||||
|
|
||||||
|
|
||||||
// Compute texture coordinates
|
// Compute texture coordinates
|
||||||
let t = atlas[animate(instance, age)];
|
let frame = animate(instance.sprite_index, age, 0.0);
|
||||||
out.texture_index = u32(t.atlas_texture);
|
out.tween = fract(frame);
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
|
||||||
|
let t = global_atlas[u32(floor(frame))];
|
||||||
|
out.texture_index_a = u32(t.atlas_texture);
|
||||||
|
out.texture_coords_a = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
out.texture_coords_a = out.texture_coords_a + vec2(t.width, 0.0);
|
||||||
}
|
}
|
||||||
if vertex.texture_coords.y == 1.0 {
|
if vertex.texture_coords.y == 1.0 {
|
||||||
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
out.texture_coords_a = out.texture_coords_a + vec2(0.0, t.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let b = global_atlas[u32(floor(animate(instance.sprite_index, age, 1.0)))];
|
||||||
|
out.texture_index_b = u32(b.atlas_texture);
|
||||||
|
out.texture_coords_b = vec2(b.xpos, b.ypos);
|
||||||
|
if vertex.texture_coords.x == 1.0 {
|
||||||
|
out.texture_coords_b = out.texture_coords_b + vec2(b.width, 0.0);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords_b = out.texture_coords_b + vec2(0.0, b.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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(
|
|
||||||
texture_array[in.texture_index],
|
return mix(
|
||||||
sampler_array[0],
|
textureSampleLevel(
|
||||||
in.texture_coords,
|
texture_array[in.texture_index_a],
|
||||||
0.0
|
sampler_array[0],
|
||||||
).rgba;
|
in.texture_coords_a,
|
||||||
|
0.0
|
||||||
|
).rgba,
|
||||||
|
textureSampleLevel(
|
||||||
|
texture_array[in.texture_index_b],
|
||||||
|
sampler_array[0],
|
||||||
|
in.texture_coords_b,
|
||||||
|
0.0
|
||||||
|
).rgba,
|
||||||
|
in.tween
|
||||||
|
);
|
||||||
}
|
}
|
|
@ -41,18 +41,18 @@ fn vertex_main(
|
||||||
// Center of the tile the camera is currently in, in game coordinates.
|
// Center of the tile the camera is currently in, in game coordinates.
|
||||||
// x div y = x - (x mod y)
|
// x div y = x - (x mod y)
|
||||||
let tile_center = (
|
let tile_center = (
|
||||||
global.camera_position.xy
|
global_data.camera_position.xy
|
||||||
- (
|
- (
|
||||||
fmod(
|
fmod(
|
||||||
global.camera_position.xy + global.starfield_tile_size.x / 2.0,
|
global_data.camera_position.xy + global_data.starfield_tile_size.x / 2.0,
|
||||||
global.starfield_tile_size.x
|
global_data.starfield_tile_size.x
|
||||||
) - global.starfield_tile_size.x / 2.0
|
) - global_data.starfield_tile_size.x / 2.0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
let zoom_min_times = (
|
let zoom_min_times = (
|
||||||
global.camera_zoom.x / global.camera_zoom_limits.x
|
global_data.camera_zoom.x / global_data.camera_zoom_limits.x
|
||||||
);
|
);
|
||||||
|
|
||||||
// Hide n% of the smallest stars
|
// Hide n% of the smallest stars
|
||||||
|
@ -64,8 +64,8 @@ fn vertex_main(
|
||||||
|
|
||||||
if (
|
if (
|
||||||
instance.size < (
|
instance.size < (
|
||||||
hide_fraction * (global.starfield_size_limits.y - global.starfield_size_limits.x)
|
hide_fraction * (global_data.starfield_size_limits.y - global_data.starfield_size_limits.x)
|
||||||
+ (global.starfield_size_limits.x)
|
+ (global_data.starfield_size_limits.x)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||||
|
@ -76,41 +76,41 @@ fn vertex_main(
|
||||||
// Apply sprite aspect ratio & scale factor
|
// Apply sprite aspect ratio & scale factor
|
||||||
// also applies screen aspect ratio
|
// also applies screen aspect ratio
|
||||||
// Note that we do NOT scale for distance here---this is intentional.
|
// Note that we do NOT scale for distance here---this is intentional.
|
||||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
var scale: f32 = instance.size / global_data.camera_zoom.x;
|
||||||
|
|
||||||
// Minimum scale to prevent flicker at large zoom levels
|
// Minimum scale to prevent flicker at large zoom levels
|
||||||
var real_size = scale * global.window_size.xy;
|
var real_size = scale * global_data.window_size.xy;
|
||||||
if (real_size.x < 2.0 || real_size.y < 2.0) {
|
if (real_size.x < 2.0 || real_size.y < 2.0) {
|
||||||
// Otherwise, clamp to a minimum scale
|
// Otherwise, clamp to a minimum scale
|
||||||
scale = 2.0 / max(global.window_size.x, global.window_size.y);
|
scale = 2.0 / max(global_data.window_size.x, global_data.window_size.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Divide by two, because viewport height is 2 in screen units
|
// Divide by two, because viewport height is 2 in screen units
|
||||||
// (coordinates go from -1 to 1)
|
// (coordinates go from -1 to 1)
|
||||||
var pos: vec2<f32> = vec2<f32>(
|
var pos: vec2<f32> = vec2<f32>(
|
||||||
vertex.position.x * (scale/2.0) / global.window_aspect.x,
|
vertex.position.x * (scale/2.0) / global_data.window_aspect.x,
|
||||||
vertex.position.y * (scale/2.0)
|
vertex.position.y * (scale/2.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
// World position relative to camera
|
// World position relative to camera
|
||||||
// (Note that instance position is in a different
|
// (Note that instance position is in a different
|
||||||
// coordinate system than usual)
|
// coordinate system than usual)
|
||||||
let camera_pos = (instance.position.xy + tile_center) - global.camera_position.xy;
|
let camera_pos = (instance.position.xy + tile_center) - global_data.camera_position.xy;
|
||||||
|
|
||||||
// Translate
|
// Translate
|
||||||
pos = pos + (
|
pos = pos + (
|
||||||
// Don't forget to correct distance for screen aspect ratio too!
|
// Don't forget to correct distance for screen aspect ratio too!
|
||||||
(camera_pos / (global.camera_zoom.x * instance.position.z))
|
(camera_pos / (global_data.camera_zoom.x * instance.position.z))
|
||||||
/ vec2<f32>(global.window_aspect.x, 1.0)
|
/ vec2<f32>(global_data.window_aspect.x, 1.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
||||||
|
|
||||||
|
|
||||||
// Starfield sprites may not be animated
|
// Starfield sprites may not be animated
|
||||||
let i = sprites[global.starfield_sprite.x].first_frame;
|
let i = global_sprites[global_data.starfield_sprite.x].first_frame;
|
||||||
let t = atlas[i];
|
let t = global_atlas[i];
|
||||||
out.texture_index = u32(t.atlas_texture);
|
out.texture_index = u32(t.atlas_texture);
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
|
|
|
@ -6,7 +6,7 @@ struct InstanceInput {
|
||||||
@location(4) transform_matrix_2: vec4<f32>,
|
@location(4) transform_matrix_2: vec4<f32>,
|
||||||
@location(5) transform_matrix_3: vec4<f32>,
|
@location(5) transform_matrix_3: vec4<f32>,
|
||||||
@location(6) color_transform: vec4<f32>,
|
@location(6) color_transform: vec4<f32>,
|
||||||
@location(7) texture_index: u32,
|
@location(7) sprite_index: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
|
@ -48,7 +48,7 @@ fn vertex_main(
|
||||||
out.color_transform = instance.color_transform;
|
out.color_transform = instance.color_transform;
|
||||||
|
|
||||||
// Pick texture frame
|
// Pick texture frame
|
||||||
let t = atlas[animate(instance, global.current_time.x)];
|
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);
|
||||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||||
if vertex.texture_coords.x == 1.0 {
|
if vertex.texture_coords.x == 1.0 {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use wgpu;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
||||||
pub struct ImageLocation {
|
pub struct AtlasImageLocation {
|
||||||
// Image box, in texture coordinates
|
// Image box, in texture coordinates
|
||||||
pub xpos: f32,
|
pub xpos: f32,
|
||||||
pub ypos: f32,
|
pub ypos: f32,
|
||||||
|
@ -20,14 +20,14 @@ pub struct ImageLocation {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct AtlasArray {
|
pub struct AtlasArray {
|
||||||
pub data: [ImageLocation; IMAGE_LIMIT as usize],
|
pub data: [AtlasImageLocation; IMAGE_LIMIT as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Pod for AtlasArray {}
|
unsafe impl Pod for AtlasArray {}
|
||||||
unsafe impl Zeroable for AtlasArray {
|
unsafe impl Zeroable for AtlasArray {
|
||||||
fn zeroed() -> Self {
|
fn zeroed() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: [ImageLocation::zeroed(); IMAGE_LIMIT as usize],
|
data: [AtlasImageLocation::zeroed(); IMAGE_LIMIT as usize],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use wgpu;
|
||||||
// all smaller values must be padded.
|
// all smaller values must be padded.
|
||||||
// also, [f32; 3] are aligned as [f32; 4]
|
// also, [f32; 3] are aligned as [f32; 4]
|
||||||
// (since alignments must be powers of two)
|
// (since alignments must be powers of two)
|
||||||
pub struct DataContent {
|
pub struct GlobalDataContent {
|
||||||
/// Camera position, in game units
|
/// Camera position, in game units
|
||||||
pub camera_position: [f32; 2],
|
pub camera_position: [f32; 2],
|
||||||
|
|
||||||
|
@ -43,6 +43,6 @@ pub struct DataContent {
|
||||||
pub current_time: [f32; 2],
|
pub current_time: [f32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataContent {
|
impl GlobalDataContent {
|
||||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use galactica_constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT, SPRITE_LIMIT};
|
use galactica_constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT, SPRITE_LIMIT};
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
use super::{object::ObjectLocationArray, AtlasArray, DataContent, SpriteDataArray};
|
use super::{object::ObjectLocationArray, AtlasArray, GlobalDataContent, SpriteDataArray};
|
||||||
|
|
||||||
pub struct GlobalUniform {
|
pub struct GlobalUniform {
|
||||||
pub data_buffer: wgpu::Buffer,
|
pub data_buffer: wgpu::Buffer,
|
||||||
|
@ -10,7 +10,7 @@ pub struct GlobalUniform {
|
||||||
pub object_buffer: wgpu::Buffer,
|
pub object_buffer: wgpu::Buffer,
|
||||||
pub bind_group: wgpu::BindGroup,
|
pub bind_group: wgpu::BindGroup,
|
||||||
pub bind_group_layout: wgpu::BindGroupLayout,
|
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||||
pub content: DataContent,
|
pub content: GlobalDataContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalUniform {
|
impl GlobalUniform {
|
||||||
|
@ -21,8 +21,8 @@ impl GlobalUniform {
|
||||||
out.push_str(&format!("@group({group}) @binding(0)\n"));
|
out.push_str(&format!("@group({group}) @binding(0)\n"));
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#"
|
r#"
|
||||||
var<uniform> global: GlobalUniform;
|
var<uniform> global_data: GlobalData;
|
||||||
struct GlobalUniform {
|
struct GlobalData {
|
||||||
camera_position: vec2<f32>,
|
camera_position: vec2<f32>,
|
||||||
camera_zoom: vec2<f32>,
|
camera_zoom: vec2<f32>,
|
||||||
camera_zoom_limits: vec2<f32>,
|
camera_zoom_limits: vec2<f32>,
|
||||||
|
@ -41,13 +41,13 @@ impl GlobalUniform {
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
r#"
|
r#"
|
||||||
@group({group}) @binding(1)
|
@group({group}) @binding(1)
|
||||||
var<uniform> atlas: array<ImageLocation, {IMAGE_LIMIT}>;
|
var<uniform> global_atlas: array<AtlasImageLocation, {IMAGE_LIMIT}>;
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
out.push_str("\n");
|
out.push_str("\n");
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#"
|
r#"
|
||||||
struct ImageLocation {
|
struct AtlasImageLocation {
|
||||||
xpos: f32,
|
xpos: f32,
|
||||||
ypos: f32,
|
ypos: f32,
|
||||||
width: f32,
|
width: f32,
|
||||||
|
@ -73,7 +73,7 @@ impl GlobalUniform {
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
r#"
|
r#"
|
||||||
@group({group}) @binding(2)
|
@group({group}) @binding(2)
|
||||||
var<uniform> sprites: array<SpriteData, {SPRITE_LIMIT}>;
|
var<uniform> global_sprites: array<SpriteData, {SPRITE_LIMIT}>;
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
out.push_str("\n");
|
out.push_str("\n");
|
||||||
|
@ -98,22 +98,22 @@ impl GlobalUniform {
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
r#"
|
r#"
|
||||||
@group({group}) @binding(3)
|
@group({group}) @binding(3)
|
||||||
var<uniform> objects: array<ObjectLocation, {OBJECT_SPRITE_INSTANCE_LIMIT}>;
|
var<uniform> objects: array<ObjectData, {OBJECT_SPRITE_INSTANCE_LIMIT}>;
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
out.push_str("\n");
|
out.push_str("\n");
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#"
|
r#"
|
||||||
struct ObjectLocation {
|
struct ObjectData {
|
||||||
xpos: f32,
|
xpos: f32,
|
||||||
ypos: f32,
|
ypos: f32,
|
||||||
zpos: f32,
|
zpos: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
size: f32,
|
size: f32,
|
||||||
|
parent: u32,
|
||||||
|
is_child: u32,
|
||||||
|
|
||||||
padding_a: f32,
|
padding_a: f32,
|
||||||
padding_b: f32,
|
|
||||||
padding_c: f32,
|
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -125,7 +125,7 @@ impl GlobalUniform {
|
||||||
pub fn new(device: &wgpu::Device) -> Self {
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
let data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
let data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("global uniform data buffer"),
|
label: Some("global uniform data buffer"),
|
||||||
size: DataContent::SIZE,
|
size: GlobalDataContent::SIZE,
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
|
@ -227,7 +227,7 @@ impl GlobalUniform {
|
||||||
object_buffer,
|
object_buffer,
|
||||||
bind_group,
|
bind_group,
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
content: DataContent::default(),
|
content: GlobalDataContent::default(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ mod globaluniform;
|
||||||
mod object;
|
mod object;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
|
|
||||||
pub use atlas::{AtlasArray, ImageLocation};
|
pub use atlas::{AtlasArray, AtlasImageLocation};
|
||||||
pub use data::DataContent;
|
pub use data::GlobalDataContent;
|
||||||
pub use globaluniform::GlobalUniform;
|
pub use globaluniform::GlobalUniform;
|
||||||
pub use object::ObjectLocation;
|
pub use object::ObjectData;
|
||||||
pub use sprite::{SpriteData, SpriteDataArray};
|
pub use sprite::{SpriteData, SpriteDataArray};
|
||||||
|
|
|
@ -5,30 +5,36 @@ use wgpu;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
||||||
pub struct ObjectLocation {
|
pub struct ObjectData {
|
||||||
pub xpos: f32,
|
pub xpos: f32,
|
||||||
pub ypos: f32,
|
pub ypos: f32,
|
||||||
pub zpos: f32,
|
pub zpos: f32,
|
||||||
pub angle: f32,
|
pub angle: f32,
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
|
|
||||||
pub _padding: [f32; 3],
|
/// Index of parent object
|
||||||
|
pub parent: u32,
|
||||||
|
|
||||||
|
/// 1 if has parent, 0 if not
|
||||||
|
pub is_child: u32,
|
||||||
|
|
||||||
|
pub _padding: [f32; 1],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectLocation {
|
impl ObjectData {
|
||||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ObjectLocationArray {
|
pub struct ObjectLocationArray {
|
||||||
pub data: [ObjectLocation; OBJECT_SPRITE_INSTANCE_LIMIT as usize],
|
pub data: [ObjectData; OBJECT_SPRITE_INSTANCE_LIMIT as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Pod for ObjectLocationArray {}
|
unsafe impl Pod for ObjectLocationArray {}
|
||||||
unsafe impl Zeroable for ObjectLocationArray {
|
unsafe impl Zeroable for ObjectLocationArray {
|
||||||
fn zeroed() -> Self {
|
fn zeroed() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: [ObjectLocation::zeroed(); OBJECT_SPRITE_INSTANCE_LIMIT as usize],
|
data: [ObjectData::zeroed(); OBJECT_SPRITE_INSTANCE_LIMIT as usize],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{EuclideanSpace, Matrix2, Matrix4, Point2, Rad, Vector3};
|
use cgmath::{EuclideanSpace, Matrix4, Point2, Rad, Vector3};
|
||||||
use galactica_constants;
|
use galactica_constants;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use std::{iter, rc::Rc};
|
use std::{iter, rc::Rc};
|
||||||
|
@ -9,9 +9,8 @@ use winit::{self, dpi::LogicalSize, window::Window};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
content,
|
content,
|
||||||
globaluniform::{DataContent, GlobalUniform, ObjectLocation},
|
globaluniform::{GlobalDataContent, GlobalUniform, ObjectData},
|
||||||
pipeline::PipelineBuilder,
|
pipeline::PipelineBuilder,
|
||||||
sprite::ObjectSubSprite,
|
|
||||||
starfield::Starfield,
|
starfield::Starfield,
|
||||||
texturearray::TextureArray,
|
texturearray::TextureArray,
|
||||||
vertexbuffer::{
|
vertexbuffer::{
|
||||||
|
@ -302,7 +301,6 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a ObjectInstance for an object and add it to `instances`.
|
/// Create a ObjectInstance for an object and add it to `instances`.
|
||||||
/// Also handles child sprites.
|
|
||||||
fn push_object_sprite(
|
fn push_object_sprite(
|
||||||
&self,
|
&self,
|
||||||
state: &RenderState,
|
state: &RenderState,
|
||||||
|
@ -328,8 +326,7 @@ impl GPUState {
|
||||||
// We take the maximum to account for rotated sprites.
|
// We take the maximum to account for rotated sprites.
|
||||||
let m = (s.size / s.pos.z) * s.sprite.aspect.max(1.0);
|
let m = (s.size / s.pos.z) * s.sprite.aspect.max(1.0);
|
||||||
|
|
||||||
// Don't draw (or compute matrices for)
|
// Don't draw sprites that are off the screen
|
||||||
// sprites that are off the screen
|
|
||||||
if pos.x < clip_ne.x - m
|
if pos.x < clip_ne.x - m
|
||||||
|| pos.y > clip_ne.y + m
|
|| pos.y > clip_ne.y + m
|
||||||
|| pos.x > clip_sw.x + m
|
|| pos.x > clip_sw.x + m
|
||||||
|
@ -338,16 +335,19 @@ impl GPUState {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let idx = instances.len();
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.global_uniform.object_buffer,
|
&self.global_uniform.object_buffer,
|
||||||
ObjectLocation::SIZE * instances.len() as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectLocation {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: s.pos.x,
|
xpos: s.pos.x,
|
||||||
ypos: s.pos.y,
|
ypos: s.pos.y,
|
||||||
zpos: s.pos.z,
|
zpos: s.pos.z,
|
||||||
angle: Rad::from(s.angle).0,
|
angle: Rad::from(s.angle).0,
|
||||||
size: s.size,
|
size: s.size,
|
||||||
|
parent: 0,
|
||||||
|
is_child: 0,
|
||||||
_padding: Default::default(),
|
_padding: Default::default(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
@ -355,62 +355,36 @@ impl GPUState {
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
instances.push(ObjectInstance {
|
instances.push(ObjectInstance {
|
||||||
sprite_index: s.sprite.get_index(),
|
sprite_index: s.sprite.get_index(),
|
||||||
object_index: instances.len() as u32,
|
object_index: idx as u32,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
/*
|
|
||||||
// Add children
|
// Add children
|
||||||
if let Some(children) = &s.children {
|
if let Some(children) = &s.children {
|
||||||
for c in children {
|
for c in children {
|
||||||
self.push_object_subsprite(&state, instances, c, pos, s.angle);
|
self.queue.write_buffer(
|
||||||
|
&self.global_uniform.object_buffer,
|
||||||
|
ObjectData::SIZE * instances.len() as u64,
|
||||||
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
|
xpos: c.pos.x,
|
||||||
|
ypos: c.pos.y,
|
||||||
|
zpos: c.pos.z,
|
||||||
|
angle: Rad::from(c.angle).0,
|
||||||
|
size: c.size,
|
||||||
|
parent: idx as u32,
|
||||||
|
is_child: 1,
|
||||||
|
_padding: Default::default(),
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
|
||||||
|
instances.push(ObjectInstance {
|
||||||
|
sprite_index: c.sprite.get_index(),
|
||||||
|
object_index: instances.len() as u32,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an object sprite's subsprite to `instances`.
|
/// Create a UiInstance for a ui sprite and add it to `instances`
|
||||||
/// Only called by `self.push_object_sprite`.
|
|
||||||
fn push_object_subsprite(
|
|
||||||
&self,
|
|
||||||
state: &RenderState,
|
|
||||||
instances: &mut Vec<ObjectInstance>,
|
|
||||||
s: &ObjectSubSprite,
|
|
||||||
parent_pos: Point2<f32>,
|
|
||||||
parent_angle: Deg<f32>,
|
|
||||||
) {
|
|
||||||
let scale = s.size / (s.pos.z * state.camera_zoom);
|
|
||||||
let sprite_aspect_and_scale =
|
|
||||||
Matrix4::from_nonuniform_scale(s.sprite.aspect * scale, scale, 1.0);
|
|
||||||
let rotate = Matrix4::from_angle_z(s.angle);
|
|
||||||
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
|
|
||||||
|
|
||||||
let ptranslate = Matrix4::from_translation(Vector3 {
|
|
||||||
x: parent_pos.x / (state.camera_zoom / 2.0) / self.window_aspect,
|
|
||||||
y: parent_pos.y / (state.camera_zoom / 2.0),
|
|
||||||
z: 0.0,
|
|
||||||
});
|
|
||||||
let protate = Matrix4::from_angle_z(parent_angle);
|
|
||||||
|
|
||||||
let translate = Matrix4::from_translation(Vector3 {
|
|
||||||
x: s.pos.x / (state.camera_zoom / 2.0) / self.window_aspect,
|
|
||||||
y: s.pos.y / (state.camera_zoom / 2.0),
|
|
||||||
z: 0.0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Order matters!
|
|
||||||
// The rightmost matrix is applied first.
|
|
||||||
let t = OPENGL_TO_WGPU_MATRIX
|
|
||||||
* ptranslate * screen_aspect
|
|
||||||
* protate * translate
|
|
||||||
* rotate * sprite_aspect_and_scale;
|
|
||||||
|
|
||||||
instances.push(ObjectInstance {
|
|
||||||
transform: t.into(),
|
|
||||||
sprite_index: s.sprite.get_index(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Create a ObjectInstance for a ui sprite and add it to `instances`
|
|
||||||
fn push_ui_sprite(&self, instances: &mut Vec<UiInstance>, 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());
|
||||||
|
@ -578,7 +552,7 @@ impl GPUState {
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.global_uniform.data_buffer,
|
&self.global_uniform.data_buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[DataContent {
|
bytemuck::cast_slice(&[GlobalDataContent {
|
||||||
camera_position: state.camera_pos.into(),
|
camera_position: state.camera_pos.into(),
|
||||||
camera_zoom: [state.camera_zoom, 0.0],
|
camera_zoom: [state.camera_zoom, 0.0],
|
||||||
camera_zoom_limits: [galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX],
|
camera_zoom_limits: [galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX],
|
||||||
|
@ -606,7 +580,8 @@ impl GPUState {
|
||||||
bytemuck::cast_slice(&[ParticleInstance {
|
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(),
|
||||||
rotation: Matrix2::from_angle(i.angle).into(),
|
angle: i.angle.0,
|
||||||
|
angvel: i.angvel.0,
|
||||||
size: i.size,
|
size: i.size,
|
||||||
sprite_index: i.sprite.get_index(),
|
sprite_index: i.sprite.get_index(),
|
||||||
created: state.current_time,
|
created: state.current_time,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::content;
|
use crate::content;
|
||||||
use cgmath::{Deg, Point2, Point3, Vector2};
|
use cgmath::{Deg, Point2, Point3, Rad, Vector2};
|
||||||
|
|
||||||
/// Instructions to create a new particle
|
/// Instructions to create a new particle
|
||||||
pub struct ParticleBuilder {
|
pub struct ParticleBuilder {
|
||||||
|
@ -12,8 +12,11 @@ pub struct ParticleBuilder {
|
||||||
/// This particle's velocity, in world coordinates
|
/// This particle's velocity, in world coordinates
|
||||||
pub velocity: Vector2<f32>,
|
pub velocity: Vector2<f32>,
|
||||||
|
|
||||||
/// This particle's angle, in world coordinates
|
/// This particle's angle
|
||||||
pub angle: Deg<f32>,
|
pub angle: Rad<f32>,
|
||||||
|
|
||||||
|
/// This particle's angular velocity (rad/sec)
|
||||||
|
pub angvel: Rad<f32>,
|
||||||
|
|
||||||
/// This particle's lifetime, in seconds
|
/// This particle's lifetime, in seconds
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
content,
|
content,
|
||||||
globaluniform::{AtlasArray, ImageLocation, SpriteData, SpriteDataArray},
|
globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray},
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck::Zeroable;
|
use bytemuck::Zeroable;
|
||||||
|
@ -129,7 +129,7 @@ impl TextureArray {
|
||||||
// Insert texture location data
|
// Insert texture location data
|
||||||
for path in &t.frames {
|
for path in &t.frames {
|
||||||
let image = ct.get_image(&path);
|
let image = ct.get_image(&path);
|
||||||
image_locations.data[image_counter as usize] = ImageLocation {
|
image_locations.data[image_counter as usize] = AtlasImageLocation {
|
||||||
xpos: image.x,
|
xpos: image.x,
|
||||||
ypos: image.y,
|
ypos: image.y,
|
||||||
width: image.w,
|
width: image.w,
|
||||||
|
|
|
@ -186,11 +186,14 @@ pub struct ParticleInstance {
|
||||||
/// World position of this particle
|
/// World position of this particle
|
||||||
pub position: [f32; 2],
|
pub position: [f32; 2],
|
||||||
|
|
||||||
/// Velocity of this sprite, in world coordinates
|
/// Velocity of this particle, in world coordinates
|
||||||
pub velocity: [f32; 2],
|
pub velocity: [f32; 2],
|
||||||
|
|
||||||
/// Rotation matrix for this sprite, in world coordinates
|
/// Angle of this particle, in radians
|
||||||
pub rotation: [[f32; 2]; 2],
|
pub angle: f32,
|
||||||
|
|
||||||
|
/// Angular velocity of this particle, rad/sec
|
||||||
|
pub angvel: f32,
|
||||||
|
|
||||||
/// The height of this particle, in world units
|
/// The height of this particle, in world units
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
|
@ -225,38 +228,39 @@ impl BufferObject for ParticleInstance {
|
||||||
shader_location: 3,
|
shader_location: 3,
|
||||||
format: wgpu::VertexFormat::Float32x2,
|
format: wgpu::VertexFormat::Float32x2,
|
||||||
},
|
},
|
||||||
// Rotation
|
// Angle
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||||
shader_location: 4,
|
shader_location: 4,
|
||||||
format: wgpu::VertexFormat::Float32x2,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
|
// Angvel
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
|
||||||
shader_location: 5,
|
shader_location: 5,
|
||||||
format: wgpu::VertexFormat::Float32x2,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
// Size
|
// Size
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
||||||
shader_location: 6,
|
shader_location: 6,
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
// Created
|
// Created
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 7]>() as wgpu::BufferAddress,
|
||||||
shader_location: 7,
|
shader_location: 7,
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
// Expires
|
// Expires
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 10]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
||||||
shader_location: 8,
|
shader_location: 8,
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
// Sprite
|
// Sprite
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
|
||||||
shader_location: 9,
|
shader_location: 9,
|
||||||
format: wgpu::VertexFormat::Uint32,
|
format: wgpu::VertexFormat::Uint32,
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,16 +100,48 @@ impl ShipCollapseSequence {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Position, adjusted for ship rotation
|
// Position, adjusted for ship rotation
|
||||||
let pos =
|
let pos = ship_pos
|
||||||
Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
+ Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
||||||
|
|
||||||
|
let velocity = {
|
||||||
|
let a = self.rng.gen_range(
|
||||||
|
-effect.velocity_scale_parent_rng
|
||||||
|
..=effect.velocity_scale_parent_rng,
|
||||||
|
);
|
||||||
|
|
||||||
|
let velocity = (effect.velocity_scale_parent + a)
|
||||||
|
* rigid_body.velocity_at_point(&point![pos.x, pos.y]);
|
||||||
|
|
||||||
|
Matrix2::from_angle(Rad(self.rng.gen_range(
|
||||||
|
-effect.direction_rng.0..=effect.direction_rng.0,
|
||||||
|
))) * Vector2 {
|
||||||
|
x: velocity.x,
|
||||||
|
y: velocity.y,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
particles.push(ParticleBuilder {
|
particles.push(ParticleBuilder {
|
||||||
sprite: effect.sprite,
|
sprite: effect.sprite,
|
||||||
pos: ship_pos + pos,
|
pos,
|
||||||
velocity: Vector2::zero(),
|
velocity,
|
||||||
angle: Deg::zero(),
|
|
||||||
lifetime: effect.lifetime,
|
angle: effect.angle
|
||||||
size: effect.size,
|
+ Rad(self
|
||||||
|
.rng
|
||||||
|
.gen_range(-effect.angle_rng.0..=effect.angle_rng.0)),
|
||||||
|
|
||||||
|
angvel: Rad(effect.angvel.0
|
||||||
|
+ self
|
||||||
|
.rng
|
||||||
|
.gen_range(-effect.angvel_rng.0..=effect.angvel_rng.0)),
|
||||||
|
|
||||||
|
lifetime: effect.lifetime
|
||||||
|
+ self
|
||||||
|
.rng
|
||||||
|
.gen_range(-effect.lifetime_rng..=effect.lifetime_rng),
|
||||||
|
|
||||||
|
size: effect.size
|
||||||
|
+ self.rng.gen_range(-effect.size_rng..=effect.size_rng),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,13 +177,15 @@ impl ShipCollapseSequence {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Position, adjusted for ship rotation
|
// Position, adjusted for ship rotation
|
||||||
let pos = Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
let pos = ship_pos + Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
|
||||||
|
let vel = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
|
||||||
|
|
||||||
particles.push(ParticleBuilder {
|
particles.push(ParticleBuilder {
|
||||||
sprite: effect.sprite,
|
sprite: effect.sprite,
|
||||||
pos: ship_pos + pos,
|
pos,
|
||||||
velocity: Vector2::zero(),
|
velocity: Vector2 { x: vel.x, y: vel.y },
|
||||||
angle: Deg::zero(),
|
angle: Rad::zero(),
|
||||||
|
angvel: Rad::zero(),
|
||||||
lifetime: effect.lifetime,
|
lifetime: effect.lifetime,
|
||||||
size: effect.size,
|
size: effect.size,
|
||||||
});
|
});
|
||||||
|
|
|
@ -133,6 +133,7 @@ impl<'a> World {
|
||||||
projectile_h: ColliderHandle,
|
projectile_h: ColliderHandle,
|
||||||
ship_h: ColliderHandle,
|
ship_h: ColliderHandle,
|
||||||
) {
|
) {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
let projectile = self.projectiles.get(&projectile_h);
|
let projectile = self.projectiles.get(&projectile_h);
|
||||||
let ship = self.ships.get_mut(&ship_h);
|
let ship = self.ships.get_mut(&ship_h);
|
||||||
if projectile.is_none() || ship.is_none() {
|
if projectile.is_none() || ship.is_none() {
|
||||||
|
@ -173,29 +174,38 @@ impl<'a> World {
|
||||||
None => {}
|
None => {}
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let x = ct.get_effect(*x);
|
let x = ct.get_effect(*x);
|
||||||
let velocity = match x.inherit_velocity {
|
|
||||||
content::ImpactInheritVelocity::Dont => Vector2 { x: 0.0, y: 0.0 },
|
let velocity = {
|
||||||
content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(pr),
|
let (_, sr) = self.get_ship_body(s).unwrap();
|
||||||
content::ImpactInheritVelocity::Target => {
|
let target_velocity =
|
||||||
// Match target ship velocity.
|
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
|
||||||
// Particles will fly off if the ship is spinning fast, but I
|
let a = rng
|
||||||
// haven't found a good way to fix that.
|
.gen_range(-x.velocity_scale_parent_rng..=x.velocity_scale_parent_rng);
|
||||||
let (_, sr) = self.get_ship_body(s).unwrap();
|
let b = rng
|
||||||
let velocity =
|
.gen_range(-x.velocity_scale_target_rng..=x.velocity_scale_target_rng);
|
||||||
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
|
|
||||||
Vector2 {
|
let velocity = ((x.velocity_scale_parent + a)
|
||||||
x: velocity.x,
|
* util::rigidbody_velocity(pr))
|
||||||
y: velocity.y,
|
+ ((x.velocity_scale_target + b)
|
||||||
}
|
* Vector2 {
|
||||||
}
|
x: target_velocity.x,
|
||||||
|
y: target_velocity.y,
|
||||||
|
});
|
||||||
|
|
||||||
|
Matrix2::from_angle(Rad(
|
||||||
|
rng.gen_range(-x.direction_rng.0..=x.direction_rng.0)
|
||||||
|
)) * velocity
|
||||||
};
|
};
|
||||||
|
|
||||||
particles.push(ParticleBuilder {
|
particles.push(ParticleBuilder {
|
||||||
sprite: x.sprite,
|
sprite: x.sprite,
|
||||||
pos: Point2 { x: pos.x, y: pos.y },
|
pos: Point2 { x: pos.x, y: pos.y },
|
||||||
velocity,
|
velocity,
|
||||||
angle: -angle,
|
angle: Rad::from(-angle)
|
||||||
lifetime: x.lifetime,
|
+ Rad(rng.gen_range(-x.angle_rng.0..=x.angle_rng.0)),
|
||||||
size: x.size,
|
angvel: Rad(x.angvel.0 + rng.gen_range(-x.angvel_rng.0..=x.angvel_rng.0)),
|
||||||
|
lifetime: x.lifetime + rng.gen_range(-x.lifetime_rng..=x.lifetime_rng),
|
||||||
|
size: x.size + rng.gen_range(-x.size_rng..=x.size_rng),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -320,6 +330,8 @@ impl<'a> World {
|
||||||
to_remove.push(*c);
|
to_remove.push(*c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
for c in to_remove {
|
for c in to_remove {
|
||||||
let (pr, p) = self.remove_projectile(c).unwrap();
|
let (pr, p) = self.remove_projectile(c).unwrap();
|
||||||
|
|
||||||
|
@ -328,22 +340,32 @@ impl<'a> World {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let x = ct.get_effect(*x);
|
let x = ct.get_effect(*x);
|
||||||
let pos = util::rigidbody_position(&pr);
|
let pos = util::rigidbody_position(&pr);
|
||||||
|
let vel = util::rigidbody_velocity(&pr);
|
||||||
let angle: Deg<f32> = util::rigidbody_rotation(&pr)
|
let angle: Deg<f32> = util::rigidbody_rotation(&pr)
|
||||||
.angle(Vector2 { x: 1.0, y: 0.0 })
|
.angle(Vector2 { x: 1.0, y: 0.0 })
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let velocity = match x.inherit_velocity {
|
let velocity = {
|
||||||
content::ImpactInheritVelocity::Target
|
let a = rng
|
||||||
| content::ImpactInheritVelocity::Dont => Vector2 { x: 0.0, y: 0.0 },
|
.gen_range(-x.velocity_scale_parent_rng..=x.velocity_scale_parent_rng);
|
||||||
content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(&pr),
|
|
||||||
|
let velocity = (x.velocity_scale_parent + a) * vel;
|
||||||
|
|
||||||
|
velocity
|
||||||
|
//Matrix2::from_angle(Rad(
|
||||||
|
// rng.gen_range(-x.direction_rng.0..=x.direction_rng.0)
|
||||||
|
//)) * velocity
|
||||||
};
|
};
|
||||||
|
|
||||||
particles.push(ParticleBuilder {
|
particles.push(ParticleBuilder {
|
||||||
sprite: x.sprite,
|
sprite: x.sprite,
|
||||||
pos: Point2 { x: pos.x, y: pos.y },
|
pos: Point2 { x: pos.x, y: pos.y },
|
||||||
velocity,
|
velocity,
|
||||||
angle: -angle,
|
angle: Rad::from(-angle)
|
||||||
lifetime: x.lifetime,
|
+ x.angle + Rad(rng.gen_range(-x.angle_rng.0..=x.angle_rng.0)),
|
||||||
size: x.size,
|
angvel: Rad(x.angvel.0 + rng.gen_range(-x.angvel_rng.0..=x.angvel_rng.0)),
|
||||||
|
lifetime: x.lifetime + rng.gen_range(-x.lifetime_rng..=x.lifetime_rng),
|
||||||
|
size: x.size + rng.gen_range(-x.size_rng..=x.size_rng),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue