Skip to main content

blade_particle/
lib.rs

1#![allow(
2    // We don't use syntax sugar where it's not necessary.
3    clippy::match_like_matches_macro,
4    // Redundant matching is more explicit.
5    clippy::redundant_pattern_matching,
6    // Explicit lifetimes are often easier to reason about.
7    clippy::needless_lifetimes,
8    // No need for defaults in the internal types.
9    clippy::new_without_default,
10    // Matches are good and extendable, no need to make an exception here.
11    clippy::single_match,
12    // Push commands are more regular than macros.
13    clippy::vec_init_then_push,
14    // This is the land of unsafe.
15    clippy::missing_safety_doc,
16)]
17#![warn(
18    trivial_numeric_casts,
19    unused_extern_crates,
20    //TODO: re-enable. Currently doesn't like "mem::size_of" on newer Rust
21    //unused_qualifications,
22    // We don't match on a reference, unless required.
23    clippy::pattern_type_mismatch,
24)]
25
26mod system;
27
28pub use system::{ParticlePipeline, ParticleSystem, PipelineDesc};
29
30#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
31pub enum EmitterShape {
32    Point,
33    Sphere { radius: f32 },
34}
35
36#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
37pub struct Emitter {
38    /// Particles emitted per second (0 = burst-only).
39    pub rate: f32,
40    /// Number of particles per burst trigger.
41    pub burst_count: u32,
42    /// Shape from which particles originate.
43    pub shape: EmitterShape,
44    /// Half-angle of the emission cone in radians.
45    /// 0 = emit only along direction, PI = full sphere (default).
46    #[serde(default = "default_cone_angle")]
47    pub cone_angle: f32,
48}
49
50fn default_cone_angle() -> f32 {
51    std::f32::consts::PI
52}
53
54#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
55pub enum ColorConfig {
56    /// Single solid color [r, g, b, a].
57    Solid([u8; 4]),
58    /// Random pick from a palette.
59    Palette(Vec<[u8; 4]>),
60}
61
62#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
63pub struct ParticleConfig {
64    /// Lifetime range in seconds [min, max].
65    pub life: [f32; 2],
66    /// Initial speed range [min, max].
67    pub speed: [f32; 2],
68    /// Scale range [min, max].
69    pub scale: [f32; 2],
70    /// Color configuration.
71    pub color: ColorConfig,
72}
73
74#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
75pub struct ParticleEffect {
76    /// Maximum number of live particles.
77    pub capacity: u32,
78    /// Emitter configuration.
79    pub emitter: Emitter,
80    /// Particle property ranges.
81    pub particle: ParticleConfig,
82}
83
84impl ParticleEffect {
85    pub fn load(source: &str) -> Result<Self, ron::error::SpannedError> {
86        ron::from_str(source)
87    }
88}
89
90/// Camera parameters for 3D particle projection.
91#[repr(C)]
92#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
93pub struct CameraParams {
94    /// Column-major 4x4 view-projection matrix.
95    pub view_proj: [f32; 16],
96    /// Camera right vector in world space (for billboard X offset).
97    pub camera_right: [f32; 4],
98    /// Camera up vector in world space (for billboard Y offset).
99    pub camera_up: [f32; 4],
100}