• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

djeedai / bevy_hanabi / 17956962574

23 Sep 2025 07:30PM UTC coverage: 66.455% (+0.2%) from 66.279%
17956962574

Pull #497

github

web-flow
Merge 1e0b6278d into fee4feaf6
Pull Request #497: Check the results of `has_side_effect` for binary expressions.

19 of 22 new or added lines in 1 file covered. (86.36%)

540 existing lines in 2 files now uncovered.

4917 of 7399 relevant lines covered (66.45%)

448.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.55
/src/graph/expr.rs
1
//! Expression API
2
//!
3
//! This module contains the low-level _Expression API_, designed to produce
4
//! highly customizable modifier behaviors through a code-first API focused on
5
//! runtime and serialization. For asset editing, the higher-level [Node API]
6
//! offers an easier-to-use abstraction built on top of the Expression API.
7
//!
8
//! # Modules and expressions
9
//!
10
//! A particle effect is composed of a series [`Modifier`]s decribing how to
11
//! initialize and update (simulate) the particles of the effect. Choosing which
12
//! modifier to add to an effect provides the user some limited level of
13
//! customizing. However modifiers alone cannot provide enough customizing to
14
//! build visual effects. For this reason, modifier inputs can be further
15
//! customized with _expressions_. An expression produces a value which is
16
//! assigned to the input. That value can be constant, in which case it will be
17
//! hard-coded into the generated WGSL shader, for performance reasons.
18
//! Alternatively, that value can vary based on other quantities, like an effect
19
//! property, a particle attribute, or some built-in simulation variable like
20
//! the simulation time.
21
//!
22
//! An expression is represented by the [`Expr`] enum. Expressions can be
23
//! combined together to form more complex expression; for example, the Add
24
//! expression computes the sum between two other expressions. [`Expr`]
25
//! represents a form of abstraction over the actual WGSL shader code, and is
26
//! generally closely related to the actual expressions of the WGSL language
27
//! itself.
28
//!
29
//! An expression often refers to other expressions. However, [`Expr`] as an
30
//! enum cannot directly contain other [`Expr`], otherwise the type would become
31
//! infinitely recursive. Instead, each expression is stored into a [`Module`]
32
//! and indexed by an [`ExprHandle`], a non-zero index referencing the
33
//! expression inside the module. This indirection avoids the recursion issue.
34
//! This means all expressions are implicitly associated with a unique module,
35
//! and care must be taken to not mix exressions from different modules.
36
//!
37
//! Each [`EffectAsset`] contains a single [`Module`] storing all the [`Expr`]
38
//! used in all its modifiers.
39
//!
40
//! # Kinds of expressions
41
//!
42
//! Expressions can be grouped into various kinds, for the sake of
43
//! comprehension:
44
//! - Literal expressions represent a constant, which will be hard-coded into
45
//!   the final WGSL shader code. Expressions like `1.42` or `vec3<f32>(0.)` are
46
//!   literal expressions in WGSL, and are represented by a [`LiteralExpr`].
47
//! - Built-in expressions represent specific built-in values provided by the
48
//!   simulation context. For example, the current simulation time is a built-in
49
//!   expression accessible from the shader code of any visual effect to animate
50
//!   it. A built-in expression is represented by a [`BuiltInExpr`].
51
//! - Attribute expressions represent the value of an attribute of a particle. A
52
//!   typical example is the particle position, represented by
53
//!   [`Attribute::POSITION`], which can be obtained as an expression through an
54
//!   [`AttributeExpr`].
55
//! - Property expressions represent the value of a visual effect property, a
56
//!   quantity assigned by the user on the CPU side and uploaded each frame into
57
//!   the GPU for precise per-frame control over an effect. It's represented by
58
//!   a [`PropertyExpr`].
59
//! - Unary and binary operations are expressions taking one or two operand
60
//!   expressions and transforming them. A typical example is the Add operator,
61
//!   which takes two operand expressions and produces their sum.
62
//!
63
//! # Building expressions
64
//!
65
//! The fundamental way to build expressions is to directly write them into a
66
//! [`Module`] itself. The [`Module`] type contains various methods to create
67
//! new expressions and immediately write them.
68
//!
69
//! ```
70
//! # use bevy_hanabi::*;
71
//! let mut module = Module::default();
72
//!
73
//! // Build and write a literal expression into the module.
74
//! let expr = module.lit(3.42);
75
//! ```
76
//!
77
//! Due to the code-first nature of the Expression API however, that approach
78
//! can be very verbose. Instead, users are encouraged to use an [`ExprWriter`],
79
//! a simple utility to build expressions with a shortened syntax. Once an
80
//! expression is built, it can be written into the underlying [`Module`]. This
81
//! approach generally makes the code more readable, and is therefore highly
82
//! encouraged, but is not mandatory.
83
//!
84
//! ```
85
//! # use bevy_hanabi::*;
86
//! // Create a writer owning a new Module
87
//! let mut w = ExprWriter::new();
88
//!
89
//! // Build a complex expression: max(3.42, properties.my_prop)
90
//! let prop = w.add_property("my_property", 3.0.into());
91
//! let expr = w.lit(3.42).max(w.prop(prop));
92
//!
93
//! // Finalize the expression and write it into the Module. The returned handle can
94
//! // be assign to a modifier input.
95
//! let handle = expr.expr();
96
//!
97
//! // Finish using the writer and recover the Module with all written expressions
98
//! let module = w.finish();
99
//! ```
100
//!
101
//! [Node API]: crate::graph::node
102
//! [`Modifier`]: crate::Modifier
103
//! [`EffectAsset`]: crate::EffectAsset
104

105
use std::{cell::RefCell, num::NonZeroU32, rc::Rc};
106

107
use bevy::{platform::collections::HashSet, prelude::default, reflect::Reflect};
108
use serde::{Deserialize, Serialize};
109
use thiserror::Error;
110

111
use super::Value;
112
use crate::{
113
    Attribute, ModifierContext, ParticleLayout, Property, PropertyLayout, ScalarType,
114
    TextureLayout, TextureSlot, ToWgslString, ValueType, VectorType,
115
};
116

117
/// A one-based ID into a collection of a [`Module`].
118
type Id = NonZeroU32;
119

120
/// Handle of an expression inside a given [`Module`].
121
///
122
/// A handle uniquely references an [`Expr`] stored inside a [`Module`]. It's a
123
/// lightweight representation, similar to a simple array index. For this
124
/// reason, it's easily copyable. However it's also lacking any kind of error
125
/// checking, and mixing handles to different modules produces undefined
126
/// behaviors (like an index does when indexing the wrong array).
127
#[derive(
128
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflect, Serialize, Deserialize,
129
)]
130
#[repr(transparent)]
131
#[serde(transparent)]
132
pub struct ExprHandle {
133
    id: Id,
134
}
135

136
impl ExprHandle {
137
    /// Create a new handle from a 1-based [`Id`].
138
    #[allow(dead_code)]
139
    fn new(id: Id) -> Self {
×
140
        Self { id }
141
    }
142

143
    /// Create a new handle from a 1-based [`Id`] as a `usize`, for cases where
144
    /// the index is known to be non-zero already.
145
    #[allow(unsafe_code)]
146
    unsafe fn new_unchecked(id: usize) -> Self {
242✔
147
        debug_assert!(id != 0);
484✔
148
        Self {
149
            id: NonZeroU32::new_unchecked(id as u32),
242✔
150
        }
151
    }
152

153
    /// Get the zero-based index into the array of the module.
154
    fn index(&self) -> usize {
407✔
155
        (self.id.get() - 1) as usize
407✔
156
    }
157
}
158

159
/// Handle of a property inside a given [`Module`].
160
///
161
/// A handle uniquely references a [`Property`] stored inside a [`Module`]. It's
162
/// a lightweight representation, similar to a simple array index. For this
163
/// reason, it's easily copyable. However it's also lacking any kind of error
164
/// checking, and mixing handles to different modules produces undefined
165
/// behaviors (like an index does when indexing the wrong array).
166
#[derive(
167
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflect, Serialize, Deserialize,
168
)]
169
#[repr(transparent)]
170
#[serde(transparent)]
171
pub struct PropertyHandle {
172
    id: Id,
173
}
174

175
impl PropertyHandle {
176
    /// Create a new handle from a 1-based [`Id`].
177
    #[allow(dead_code)]
178
    fn new(id: Id) -> Self {
×
179
        Self { id }
180
    }
181

182
    /// Create a new handle from a 1-based [`Id`] as a `usize`, for cases where
183
    /// the index is known to be non-zero already.
184
    #[allow(unsafe_code)]
185
    unsafe fn new_unchecked(id: usize) -> Self {
7✔
186
        debug_assert!(id != 0);
14✔
187
        Self {
188
            id: NonZeroU32::new_unchecked(id as u32),
7✔
189
        }
190
    }
191

192
    /// Get the zero-based index into the array of the module.
193
    fn index(&self) -> usize {
2✔
194
        (self.id.get() - 1) as usize
2✔
195
    }
196
}
197

198
/// Handle of a texture inside a given [`Module`].
199
///
200
/// A handle uniquely references a [`TextureSlot`] stored inside a [`Module`].
201
/// It's a lightweight representation, similar to a simple array index. For this
202
/// reason, it's easily copyable. However it's also lacking any kind of error
203
/// checking, and mixing handles to different modules produces undefined
204
/// behaviors (like an index does when indexing the wrong array).
205
#[derive(
206
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflect, Serialize, Deserialize,
207
)]
208
#[repr(transparent)]
209
#[serde(transparent)]
210
pub struct TextureHandle {
211
    id: Id,
212
}
213

214
impl TextureHandle {
215
    /// Create a new handle from a 1-based [`Id`].
216
    #[allow(dead_code)]
217
    fn new(id: Id) -> Self {
×
218
        Self { id }
219
    }
220

221
    /// Create a new handle from a 1-based [`Id`] as a `usize`, for cases where
222
    /// the index is known to be non-zero already.
223
    #[allow(unsafe_code)]
224
    unsafe fn new_unchecked(id: usize) -> Self {
×
225
        debug_assert!(id != 0);
×
226
        Self {
227
            id: NonZeroU32::new_unchecked(id as u32),
×
228
        }
229
    }
230

231
    /// Get the zero-based index into the array of the module.
232
    #[allow(dead_code)]
233
    fn index(&self) -> usize {
×
234
        (self.id.get() - 1) as usize
×
235
    }
236
}
237

238
/// Container for expressions.
239
///
240
/// A module represents a storage for a set of expressions used in a single
241
/// [`EffectAsset`]. Modules are not reusable accross effect assets; each effect
242
/// asset owns a single module, containing all the expressions used in all the
243
/// modifiers attached to that asset. However, for convenience, a module can be
244
/// cloned into an unrelated module, and the clone can be assigned to another
245
/// effect asset.
246
///
247
/// Modules are built incrementally. Expressions are written into the module
248
/// through convenience helpers like [`lit()`] or [`attr()`]. Alternatively, an
249
/// [`ExprWriter`] can be used to populate a new or existing module. Either way,
250
/// once an expression is written into a module, it cannot be modified or
251
/// deleted. Modules are not designed to be used as editing structures, but as
252
/// storage and serialization ones.
253
///
254
/// [`EffectAsset`]: crate::EffectAsset
255
/// [`lit()`]: Module::lit
256
/// [`attr()`]: Module::attr
257
#[derive(Debug, Default, Clone, PartialEq, Hash, Reflect, Serialize, Deserialize)]
258
pub struct Module {
259
    /// Expressions defined in the module.
260
    expressions: Vec<Expr>,
261
    /// Properties used as part of a [`PropertyExpr`].
262
    properties: Vec<Property>,
263
    /// Texture layout.
264
    texture_layout: TextureLayout,
265
}
266

267
macro_rules! impl_module_unary {
268
    ($t: ident, $T: ident) => {
269
        #[doc = concat!("Build a [`UnaryOperator::", stringify!($T), "`](crate::graph::expr::UnaryOperator::", stringify!($T),") unary expression and append it to the module.\n\nThis is a shortcut for [`unary(UnaryOperator::", stringify!($T), ", inner)`](crate::graph::expr::Module::unary).")]
270
        #[inline]
271
        pub fn $t(&mut self, inner: ExprHandle) -> ExprHandle {
38✔
272
            self.unary(UnaryOperator::$T, inner)
152✔
273
        }
274
    };
275
}
276

277
macro_rules! impl_module_binary {
278
    ($t: ident, $T: ident) => {
279
        #[doc = concat!("Build a [`BinaryOperator::", stringify!($T), "`](crate::graph::expr::BinaryOperator::", stringify!($T),") binary expression and append it to the module.\n\nThis is a shortcut for [`binary(BinaryOperator::", stringify!($T), ", left, right)`](crate::graph::expr::Module::binary).")]
280
        #[inline]
281
        pub fn $t(&mut self, left: ExprHandle, right: ExprHandle) -> ExprHandle {
34✔
282
            self.binary(BinaryOperator::$T, left, right)
170✔
283
        }
284
    };
285
}
286

287
macro_rules! impl_module_ternary {
288
    ($t: ident, $T: ident) => {
289
        #[doc = concat!("Build a [`TernaryOperator::", stringify!($T), "`](crate::graph::expr::TernaryOperator::", stringify!($T),") ternary expression and append it to the module.\n\nThis is a shortcut for [`ternary(TernaryOperator::", stringify!($T), ", first, second, third)`](crate::graph::expr::Module::ternary).")]
290
        #[inline]
291
        pub fn $t(&mut self, first: ExprHandle, second: ExprHandle, third: ExprHandle) -> ExprHandle {
3✔
292
            self.ternary(TernaryOperator::$T, first, second, third)
18✔
293
        }
294
    };
295
}
296

297
impl Module {
298
    /// Create a new module from an existing collection of expressions.
299
    pub fn from_raw(expr: Vec<Expr>) -> Self {
×
300
        Self {
301
            expressions: expr,
302
            properties: vec![],
×
303
            texture_layout: default(),
×
304
        }
305
    }
306

307
    /// Add a new property to the module.
308
    ///
309
    /// See [`Property`] for more details on what effect properties are.
310
    ///
311
    /// # Panics
312
    ///
313
    /// Panics if a property with the same name already exists.
314
    pub fn add_property(
7✔
315
        &mut self,
316
        name: impl Into<String>,
317
        default_value: Value,
318
    ) -> PropertyHandle {
319
        let name = name.into();
21✔
320
        assert!(!self.properties.iter().any(|p| p.name() == name));
23✔
321
        self.properties.push(Property::new(name, default_value));
7✔
322
        // SAFETY - We just pushed a new property into the array, so its length is
323
        // non-zero.
324
        #[allow(unsafe_code)]
×
325
        unsafe {
326
            PropertyHandle::new_unchecked(self.properties.len())
×
327
        }
328
    }
329

330
    /// Get an existing property by handle.
331
    ///
332
    /// Existing properties are properties previously created with
333
    /// [`add_property()`].
334
    ///
335
    /// [`add_property()`]: crate::Module::add_property
336
    pub fn get_property(&self, property: PropertyHandle) -> Option<&Property> {
2✔
337
        self.properties.get(property.index())
6✔
338
    }
339

340
    /// Get an existing property by name.
341
    ///
342
    /// Existing properties are properties previously created with
343
    /// [`add_property()`].
344
    ///
345
    /// [`add_property()`]: crate::Module::add_property
346
    pub fn get_property_by_name(&self, name: &str) -> Option<PropertyHandle> {
×
347
        self.properties
×
348
            .iter()
349
            .enumerate()
350
            .find(|(_, prop)| prop.name() == name)
×
351
            .map(|(index, _)| PropertyHandle::new(NonZeroU32::new(index as u32 + 1).unwrap()))
×
352
    }
353

354
    /// Get the list of existing properties.
355
    pub fn properties(&self) -> &[Property] {
1,028✔
356
        &self.properties
1,028✔
357
    }
358

359
    /// Add a new texture slot to the module.
360
    ///
361
    /// The `name` parameter defines a new [`TextureSlot`] associated with this
362
    /// module, and must be unique inside this module. A unique index is also
363
    /// associated with the slot, corresponding to the value returned by
364
    /// [`TextureLayout::get_slot_by_name()`].
365
    ///
366
    /// Once a slot is added, an actual texture resource, defined by its
367
    /// `Handle<Image>`, is bound to that slot through the
368
    /// [`EffectMaterial`] component. Expressions like [`TextureSampleExpr`] can
369
    /// be used to sample the texture using the returned [`TextureHandle`].
370
    ///
371
    /// # Returns
372
    ///
373
    /// The handle of the texture inside this module. This handle is used in
374
    /// expressions like the [`TextureSampleExpr`] to reference this texture
375
    /// slot.
376
    ///
377
    /// # Panics
378
    ///
379
    /// Panics if a texture slot with the same name already exists inside this
380
    /// module. Different effect asset (different modules) can have the same
381
    /// slot name.
382
    ///
383
    /// [`EffectMaterial`]: crate::EffectMaterial
384
    pub fn add_texture_slot(&mut self, name: impl Into<String>) -> TextureHandle {
×
385
        let name = name.into();
×
386
        assert!(!self.texture_layout.layout.iter().any(|t| t.name == name));
×
387
        self.texture_layout.layout.push(TextureSlot { name });
×
388
        // SAFETY - We just pushed a new slot into the array, so its length is non-zero.
389
        #[allow(unsafe_code)]
×
390
        unsafe {
391
            TextureHandle::new_unchecked(self.texture_layout.layout.len())
×
392
        }
393
    }
394

395
    /// Insert into the given set all attributes referenced by any
396
    /// [`AttributeExpr`] present in the module.
397
    pub fn gather_attributes(&self, set: &mut HashSet<Attribute>) {
2,050✔
398
        for expr in &self.expressions {
10,152✔
399
            if let Expr::Attribute(attr) = expr {
1✔
400
                set.insert(attr.attr);
401
            }
402
        }
403
    }
404

405
    /// Append a new expression to the module.
406
    fn push(&mut self, expr: impl Into<Expr>) -> ExprHandle {
241✔
407
        self.expressions.push(expr.into());
964✔
408
        #[allow(unsafe_code)]
×
409
        unsafe {
410
            ExprHandle::new_unchecked(self.expressions.len())
723✔
411
        }
412
    }
413

414
    /// Build a literal expression and append it to the module.
415
    #[inline]
416
    pub fn lit<V>(&mut self, value: V) -> ExprHandle
79✔
417
    where
418
        Value: From<V>,
419
    {
420
        self.push(Expr::Literal(LiteralExpr::new(value)))
237✔
421
    }
422

423
    /// Build an attribute expression and append it to the module.
424
    #[inline]
425
    pub fn attr(&mut self, attr: Attribute) -> ExprHandle {
31✔
426
        self.push(Expr::Attribute(AttributeExpr::new(attr)))
93✔
427
    }
428

429
    /// Build a parent attribute expression and append it to the module.
430
    #[inline]
431
    pub fn parent_attr(&mut self, attr: Attribute) -> ExprHandle {
×
432
        self.push(Expr::ParentAttribute(AttributeExpr::new(attr)))
×
433
    }
434

435
    /// Build a property expression and append it to the module.
436
    ///
437
    /// A property expression retrieves the value of the given property.
438
    #[inline]
439
    pub fn prop(&mut self, property: PropertyHandle) -> ExprHandle {
3✔
440
        self.push(Expr::Property(PropertyExpr::new(property)))
9✔
441
    }
442

443
    /// Build a built-in expression and append it to the module.
444
    #[inline]
445
    pub fn builtin(&mut self, op: BuiltInOperator) -> ExprHandle {
30✔
446
        self.push(Expr::BuiltIn(BuiltInExpr::new(op)))
90✔
447
    }
448

449
    /// Build a unary expression and append it to the module.
450
    ///
451
    /// The handle to the expression representing the operand of the unary
452
    /// operation must be valid, that is reference an expression
453
    /// contained in the current [`Module`].
454
    ///
455
    /// # Panics
456
    ///
457
    /// Panics in some cases if the operand handle do not reference an existing
458
    /// expression in the current module. Note however that this check can
459
    /// miss some invalid handles (false negative), so only represents an
460
    /// extra safety net that users shouldn't rely exclusively on
461
    /// to ensure the operand handles are valid. Instead, it's the
462
    /// responsibility of the user to ensure the operand handle references an
463
    /// existing expression in the current [`Module`].
464
    #[inline]
465
    pub fn unary(&mut self, op: UnaryOperator, inner: ExprHandle) -> ExprHandle {
38✔
466
        assert!(inner.index() < self.expressions.len());
152✔
467
        self.push(Expr::Unary { op, expr: inner })
114✔
468
    }
469

470
    impl_module_unary!(abs, Abs);
471
    impl_module_unary!(acos, Acos);
472
    impl_module_unary!(asin, Asin);
473
    impl_module_unary!(atan, Atan);
474
    impl_module_unary!(all, All);
475
    impl_module_unary!(any, Any);
476
    impl_module_unary!(ceil, Ceil);
477
    impl_module_unary!(cos, Cos);
478
    impl_module_unary!(exp, Exp);
479
    impl_module_unary!(exp2, Exp2);
480
    impl_module_unary!(floor, Floor);
481
    impl_module_unary!(fract, Fract);
482
    impl_module_unary!(inverse_sqrt, InvSqrt);
483
    impl_module_unary!(length, Length);
484
    impl_module_unary!(log, Log);
485
    impl_module_unary!(log2, Log2);
486
    impl_module_unary!(normalize, Normalize);
487
    impl_module_unary!(pack4x8snorm, Pack4x8snorm);
488
    impl_module_unary!(pack4x8unorm, Pack4x8unorm);
489
    impl_module_unary!(round, Round);
490
    impl_module_unary!(saturate, Saturate);
491
    impl_module_unary!(sign, Sign);
492
    impl_module_unary!(sin, Sin);
493
    impl_module_unary!(sqrt, Sqrt);
494
    impl_module_unary!(tan, Tan);
495
    impl_module_unary!(unpack4x8snorm, Unpack4x8snorm);
496
    impl_module_unary!(unpack4x8unorm, Unpack4x8unorm);
497
    impl_module_unary!(w, W);
498
    impl_module_unary!(x, X);
499
    impl_module_unary!(y, Y);
500
    impl_module_unary!(z, Z);
501

502
    /// Build a binary expression and append it to the module.
503
    ///
504
    /// The handles to the expressions representing the left and right operands
505
    /// of the binary operation must be valid, that is reference expressions
506
    /// contained in the current [`Module`].
507
    ///
508
    /// # Panics
509
    ///
510
    /// Panics in some cases if either of the left or right operand handles do
511
    /// not reference existing expressions in the current module. Note however
512
    /// that this check can miss some invalid handles (false negative), so only
513
    /// represents an extra safety net that users shouldn't rely exclusively on
514
    /// to ensure the operand handles are valid. Instead, it's the
515
    /// responsibility of the user to ensure handles reference existing
516
    /// expressions in the current [`Module`].
517
    #[inline]
518
    pub fn binary(
34✔
519
        &mut self,
520
        op: BinaryOperator,
521
        left: ExprHandle,
522
        right: ExprHandle,
523
    ) -> ExprHandle {
524
        assert!(left.index() < self.expressions.len());
136✔
525
        assert!(right.index() < self.expressions.len());
136✔
526
        self.push(Expr::Binary { op, left, right })
136✔
527
    }
528

529
    impl_module_binary!(add, Add);
530
    impl_module_binary!(atan2, Atan2);
531
    impl_module_binary!(cross, Cross);
532
    impl_module_binary!(distance, Distance);
533
    impl_module_binary!(div, Div);
534
    impl_module_binary!(dot, Dot);
535
    impl_module_binary!(ge, GreaterThanOrEqual);
536
    impl_module_binary!(gt, GreaterThan);
537
    impl_module_binary!(le, LessThanOrEqual);
538
    impl_module_binary!(lt, LessThan);
539
    impl_module_binary!(max, Max);
540
    impl_module_binary!(min, Min);
541
    impl_module_binary!(mul, Mul);
542
    impl_module_binary!(rem, Remainder);
543
    impl_module_binary!(step, Step);
544
    impl_module_binary!(sub, Sub);
545
    impl_module_binary!(uniform, UniformRand);
546
    impl_module_binary!(normal, NormalRand);
547
    impl_module_binary!(vec2, Vec2);
548

549
    /// Build a ternary expression and append it to the module.
550
    ///
551
    /// The handles to the expressions representing the three operands of the
552
    /// ternary operation must be valid, that is reference expressions
553
    /// contained in the current [`Module`].
554
    ///
555
    /// # Panics
556
    ///
557
    /// Panics in some cases if any of the operand handles do not reference
558
    /// existing expressions in the current module. Note however
559
    /// that this check can miss some invalid handles (false negative), so only
560
    /// represents an extra safety net that users shouldn't rely exclusively on
561
    /// to ensure the operand handles are valid. Instead, it's the
562
    /// responsibility of the user to ensure handles reference existing
563
    /// expressions in the current [`Module`].
564
    #[inline]
565
    pub fn ternary(
3✔
566
        &mut self,
567
        op: TernaryOperator,
568
        first: ExprHandle,
569
        second: ExprHandle,
570
        third: ExprHandle,
571
    ) -> ExprHandle {
572
        assert!(first.index() < self.expressions.len());
12✔
573
        assert!(second.index() < self.expressions.len());
12✔
574
        assert!(third.index() < self.expressions.len());
12✔
575
        self.push(Expr::Ternary {
9✔
576
            op,
6✔
577
            first,
6✔
578
            second,
3✔
579
            third,
3✔
580
        })
581
    }
582

583
    impl_module_ternary!(mix, Mix);
584
    impl_module_ternary!(smoothstep, SmoothStep);
585

586
    /// Build a cast expression and append it to the module.
587
    ///
588
    /// The handle to the expressions representing the operand of the cast
589
    /// operation must be valid, that is reference expressions contained in
590
    /// the current [`Module`].
591
    ///
592
    /// # Panics
593
    ///
594
    /// Panics in some cases if the operand handle does not reference existing
595
    /// expressions in the current module. Note however that this check can
596
    /// miss some invalid handles (false negative), so only represents an
597
    /// extra safety net that users shouldn't rely exclusively on
598
    /// to ensure the operand handles are valid. Instead, it's the
599
    /// responsibility of the user to ensure handles reference existing
600
    /// expressions in the current [`Module`].
601
    ///
602
    /// Panics if the resulting cast expression is not valid. See
603
    /// [`CastExpr::is_valid()`] for the exact meaning.
604
    pub fn cast(&mut self, expr: ExprHandle, target: impl Into<ValueType>) -> ExprHandle {
9✔
605
        assert!(expr.index() < self.expressions.len());
36✔
606
        let target = target.into();
27✔
607
        let expr = CastExpr::new(expr, target);
36✔
608
        if let Some(valid) = expr.is_valid(self) {
27✔
609
            assert!(valid);
×
610
        }
611
        self.push(Expr::Cast(expr))
4✔
612
    }
613

614
    /// Get an existing expression from its handle.
615
    #[inline]
616
    pub fn get(&self, expr: ExprHandle) -> Option<&Expr> {
14✔
617
        let index = expr.index();
42✔
618
        self.expressions.get(index)
28✔
619
    }
620

621
    /// Get an existing expression from its handle.
622
    #[inline]
623
    pub fn get_mut(&mut self, expr: ExprHandle) -> Option<&mut Expr> {
2✔
624
        let index = expr.index();
6✔
625
        self.expressions.get_mut(index)
4✔
626
    }
627

628
    /// Get an existing expression from its handle.
629
    #[inline]
630
    pub fn try_get(&self, expr: ExprHandle) -> Result<&Expr, ExprError> {
265✔
631
        let index = expr.index();
795✔
632
        self.expressions
530✔
633
            .get(index)
265✔
634
            .ok_or(ExprError::InvalidExprHandleError(format!(
530✔
635
                "Cannot find expression with handle {:?} in the current module. Check that the Module used to build the expression was the same used in the EvalContext or the original EffectAsset.", expr)))
265✔
636
    }
637

638
    /// Get an existing expression from its handle.
639
    #[inline]
640
    pub fn try_get_mut(&mut self, expr: ExprHandle) -> Result<&mut Expr, ExprError> {
2✔
641
        let index = expr.index();
6✔
642
        self.expressions
4✔
643
            .get_mut(index)
2✔
644
            .ok_or(ExprError::InvalidExprHandleError(format!(
4✔
645
                "Cannot find expression with handle {:?} in the current module. Check that the Module used to build the expression was the same used in the EvalContext or the original EffectAsset.", expr)))
2✔
646
    }
647

648
    /// Is the expression resulting in a compile-time constant which can be
649
    /// hard-coded into a shader's code?
650
    ///
651
    /// # Panics
652
    ///
653
    /// Panics if `expr` doesn't refer to an expression of this module.
654
    #[inline]
655
    pub fn is_const(&self, expr: ExprHandle) -> bool {
×
656
        let expr = self.get(expr).unwrap();
×
657
        expr.is_const(self)
×
658
    }
659

660
    /// Has the expression any side-effect?
661
    ///
662
    /// Expressions with side-effect need to be stored into temporary variables
663
    /// when the shader code is emitted, so that the side effect is only applied
664
    /// once when the expression is reused in multiple locations.
665
    ///
666
    /// # Panics
667
    ///
668
    /// Panics if `expr` doesn't refer to an expression of this module.
669
    pub fn has_side_effect(&self, expr: ExprHandle) -> bool {
×
670
        let expr = self.get(expr).unwrap();
×
671
        expr.has_side_effect(self)
×
672
    }
673

674
    /// Get the texture layout of this module.
675
    pub fn texture_layout(&self) -> TextureLayout {
1,042✔
676
        self.texture_layout.clone()
2,084✔
677
    }
678
}
679

680
/// Errors raised when manipulating expressions [`Expr`] and node graphs
681
/// [`Graph`].
682
///
683
/// [`Graph`]: crate::graph::Graph
684
#[derive(Debug, Clone, PartialEq, Eq, Error)]
685
pub enum ExprError {
686
    /// Expression type error.
687
    ///
688
    /// Generally used for invalid type conversion (casting).
689
    #[error("Type error: {0}")]
690
    TypeError(String),
691

692
    /// Expression syntax error.
693
    #[error("Syntax error: {0}")]
694
    SyntaxError(String),
695

696
    /// Generic graph evaluation error.
697
    #[error("Graph evaluation error: {0}")]
698
    GraphEvalError(String),
699

700
    /// Error resolving a property.
701
    ///
702
    /// An unknown property was not defined in the evaluation context, which
703
    /// usually means that the property was not defined with
704
    /// [`Module::add_property()`].
705
    #[error("Property error: {0}")]
706
    PropertyError(String),
707

708
    /// Invalid expression handle not referencing any existing [`Expr`] in the
709
    /// evaluation [`Module`].
710
    ///
711
    /// This error is commonly raised when using an [`ExprWriter`] and
712
    /// forgetting to transfer the underlying [`Module`] where the expressions
713
    /// are written to the [`EffectAsset`]. See [`ExprWriter`] for details.
714
    ///
715
    /// [`EffectAsset`]: crate::EffectAsset
716
    #[error("Invalid expression handle: {0}")]
717
    InvalidExprHandleError(String),
718

719
    /// Invalid modifier context.
720
    ///
721
    /// The operation was expecting a given [`ModifierContext`], but instead
722
    /// another [`ModifierContext`] was available.
723
    #[error("Invalid modifier context {0}, expected {1} instead.")]
724
    InvalidModifierContext(ModifierContext, ModifierContext),
725
}
726

727
/// Evaluation context for transforming expressions into WGSL code.
728
///
729
/// The evaluation context references a [`Module`] storing all [`Expr`] in use,
730
/// as well as a [`ParticleLayout`] defining the existing attributes of each
731
/// particle and their layout in memory, and a [`PropertyLayout`] defining
732
/// existing properties and their layout in memory. These together define the
733
/// context within which expressions are evaluated.
734
///
735
/// A same expression can be valid in one context and invalid in another. The
736
/// most obvious example is a [`PropertyExpr`] which is only valid if the
737
/// property is actually defined in the property layout of the evaluation
738
/// context.
739
pub trait EvalContext {
740
    /// Get the modifier context of the evaluation.
741
    fn modifier_context(&self) -> ModifierContext;
742

743
    /// Get the particle layout of the effect.
744
    fn particle_layout(&self) -> &ParticleLayout;
745

746
    /// Get the property layout of the effect.
747
    fn property_layout(&self) -> &PropertyLayout;
748

749
    /// Evaluate an expression, returning its WGSL shader code.
750
    ///
751
    /// The evaluation is guaranteed to be unique. Calling `eval()` multiple
752
    /// times with the same handle will return the same result. If the
753
    /// expression referenced by `handle` has side effects, it's evaluated only
754
    /// once on first call, stored in a local variable, and the variable cached
755
    /// and returned on subsequent calls.
756
    fn eval(&mut self, module: &Module, handle: ExprHandle) -> Result<String, ExprError>;
757

758
    /// Generate a unique local variable name.
759
    ///
760
    /// Each time this function is called, a new unique name is generated. The
761
    /// name is guaranteed to be unique within the current evaluation context
762
    /// only. Do not use for global top-level identifiers.
763
    ///
764
    /// The variable name is not registered automatically in the [`Module`]. If
765
    /// you call `make_local_var()` but doesn't use the returned name, it won't
766
    /// appear in the shader.
767
    fn make_local_var(&mut self) -> String;
768

769
    /// Push an intermediate statement during an evaluation.
770
    ///
771
    /// Intermediate statements are inserted before the expression evaluation
772
    /// which produced them. They're generally used to define temporary local
773
    /// variables, for example to store the result of expressions with side
774
    /// effects.
775
    fn push_stmt(&mut self, stmt: &str);
776

777
    /// Create a function.
778
    ///
779
    /// Create a new function with the given `func_name` inside the given
780
    /// [`Module`]. The function takes a list of arguments `args`, which are
781
    /// copied verbatim into the shader code without any validation. The body of
782
    /// the function is generated by invoking the given closure once with the
783
    /// input `module` and a temporary [`EvalContext`] local to the function.
784
    /// The closure must return the generated shader code of the function
785
    /// body. Any statement pushed to the temporary function context with
786
    /// [`EvalContext::push_stmt()`] is emitted inside the function body before
787
    /// the returned code. The function can subsequently be called from the
788
    /// parent context by generating code to call `func_name`, with the correct
789
    /// arguments.
790
    fn make_fn(
791
        &mut self,
792
        func_name: &str,
793
        args: &str,
794
        module: &mut Module,
795
        f: &mut dyn FnMut(&mut Module, &mut dyn EvalContext) -> Result<String, ExprError>,
796
    ) -> Result<(), ExprError>;
797

798
    /// Check if the particle attribute struct is a pointer?
799
    ///
800
    /// In some context the attribute struct (named 'particle' in WGSL code) is
801
    /// a pointer instead of being a struct instance. This happens in particular
802
    /// when defining a function for a modifier, and passing the attribute
803
    /// struct to be modified. In that case the generated code needs to emit
804
    /// a pointer indirection code to access the fields of the struct.
805
    fn is_attribute_pointer(&self) -> bool;
806
}
807

808
/// Language expression producing a value.
809
#[derive(Debug, Clone, Copy, PartialEq, Hash, Reflect, Serialize, Deserialize)]
810
pub enum Expr {
811
    /// Built-in expression ([`BuiltInExpr`]).
812
    ///
813
    /// A built-in expression provides access to some internal
814
    /// quantities like the simulation time.
815
    BuiltIn(BuiltInExpr),
816

817
    /// Literal expression ([`LiteralExpr`]).
818
    ///
819
    /// A literal expression represents a shader constants.
820
    Literal(LiteralExpr),
821

822
    /// Property expression ([`PropertyExpr`]).
823
    ///
824
    /// A property expression represents the value of an [`EffectAsset`]'s
825
    /// property.
826
    ///
827
    /// [`EffectAsset`]: crate::EffectAsset
828
    Property(PropertyExpr),
829

830
    /// Attribute expression ([`AttributeExpr`]).
831
    ///
832
    /// An attribute expression represents the value of an attribute for a
833
    /// particle, like its position or velocity.
834
    Attribute(AttributeExpr),
835

836
    /// Attribute expression ([`AttributeExpr`]) from a parent effect.
837
    ///
838
    /// An attribute expression represents the value of an attribute for a
839
    /// particle, like its position or velocity. This attribute however refers
840
    /// to the parent particle which spawned this one, via GPU events.
841
    ///
842
    /// This attribute is only valid when used in init modifiers, from an effect
843
    /// with a parent effect (an effect which has an [`EffectParent`]
844
    /// component).
845
    ///
846
    /// [`EffectParent`]: crate::EffectParent
847
    ParentAttribute(AttributeExpr),
848

849
    /// Unary operation expression.
850
    ///
851
    /// A unary operation transforms an expression into another expression.
852
    Unary {
853
        /// Unary operator.
854
        op: UnaryOperator,
855
        /// Operand the unary operation applies to.
856
        expr: ExprHandle,
857
    },
858

859
    /// Binary operation expression.
860
    ///
861
    /// A binary operation composes two expressions into a third one.
862
    Binary {
863
        /// Binary operator.
864
        op: BinaryOperator,
865
        /// Left-hand side operand the binary operation applies to.
866
        left: ExprHandle,
867
        /// Right-hand side operand the binary operation applies to.
868
        right: ExprHandle,
869
    },
870

871
    /// Ternary operation expression.
872
    ///
873
    /// A ternary operation composes three expressions into a fourth one.
874
    Ternary {
875
        /// Ternary operator.
876
        op: TernaryOperator,
877
        /// First operand the ternary operation applies to.
878
        first: ExprHandle,
879
        /// Second operand the ternary operation applies to.
880
        second: ExprHandle,
881
        /// Third operand the ternary operation applies to.
882
        third: ExprHandle,
883
    },
884

885
    /// Cast expression.
886
    ///
887
    /// An expression to cast an expression to another type.
888
    Cast(CastExpr),
889

890
    /// Access to textures.
891
    ///
892
    /// An expression to sample a texture from the effect's material. Currently
893
    /// only color textures (returning a `vec4<f32>`) are supported.
894
    TextureSample(TextureSampleExpr),
895
}
896

897
impl Expr {
898
    /// Is the expression resulting in a compile-time constant which can be
899
    /// hard-coded into a shader's code?
900
    ///
901
    /// The [`Module`] passed as argument is the owning module of the
902
    /// expression, which is used to recursively evaluate the const-ness of the
903
    /// entire expression. For some expressions like literals or attributes or
904
    /// properties their const-ness is intrinsic to the expression type, but for
905
    /// other expressions like binary operations (addition, ...) their
906
    /// const-ness depends in turn on the const-ness of their sub-expressions
907
    /// (left and right operands), which requires a [`Module`] to be retrieved
908
    /// and evaluated.
909
    ///
910
    /// # Example
911
    ///
912
    /// ```
913
    /// # use bevy_hanabi::*;
914
    /// # let mut module = Module::default();
915
    /// // Literals are always constant by definition.
916
    /// assert!(Expr::Literal(LiteralExpr::new(1.)).is_const(&module));
917
    ///
918
    /// // Properties and attributes are never constant, since they're by definition used
919
    /// // to provide runtime customization.
920
    /// let prop = module.add_property("my_property", 3.0.into());
921
    /// assert!(!Expr::Property(PropertyExpr::new(prop)).is_const(&module));
922
    /// assert!(!Expr::Attribute(AttributeExpr::new(Attribute::POSITION)).is_const(&module));
923
    /// ```
924
    pub fn is_const(&self, module: &Module) -> bool {
×
925
        match self {
×
926
            Expr::BuiltIn(expr) => expr.is_const(),
×
927
            Expr::Literal(expr) => expr.is_const(),
×
928
            Expr::Property(expr) => expr.is_const(),
×
929
            Expr::Attribute(expr) => expr.is_const(),
×
930
            Expr::ParentAttribute(expr) => expr.is_const(),
×
931
            Expr::Unary { expr, .. } => module.is_const(*expr),
×
932
            Expr::Binary { left, right, .. } => module.is_const(*left) && module.is_const(*right),
×
933
            Expr::Ternary {
934
                first,
×
935
                second,
×
936
                third,
×
937
                ..
938
            } => module.is_const(*first) && module.is_const(*second) && module.is_const(*third),
×
939
            Expr::Cast(expr) => module.is_const(expr.inner),
×
940
            Expr::TextureSample(_) => false,
×
941
        }
942
    }
943

944
    /// Has the expression any side-effect?
945
    ///
946
    /// Expressions with side-effect need to be stored into temporary variables
947
    /// when the shader code is emitted, so that the side effect is only applied
948
    /// once when the expression is reused in multiple locations.
949
    pub fn has_side_effect(&self, _: &Module) -> bool {
39✔
950
        match self {
39✔
951
            Expr::BuiltIn(expr) => expr.has_side_effect(),
×
952
            Expr::Literal(_) => false,
×
953
            Expr::Property(_) => false,
×
954
            Expr::Attribute(_) => false,
×
955
            Expr::ParentAttribute(_) => false,
×
NEW
956
            Expr::Unary { .. } => false,
×
957
            Expr::Binary { op, .. } => {
39✔
958
                *op == BinaryOperator::UniformRand || *op == BinaryOperator::NormalRand
78✔
959
            }
NEW
960
            Expr::Ternary { .. } => false,
×
NEW
961
            Expr::Cast(_) => false,
×
UNCOV
962
            Expr::TextureSample(_) => false,
×
963
        }
964
    }
965

966
    /// The type of the value produced by the expression.
967
    ///
968
    /// If the type is variable and depends on the runtime evaluation context,
969
    /// this returns `None`. In that case the type needs to be obtained by
970
    /// evaluating the expression with [`eval()`].
971
    ///
972
    /// # Example
973
    ///
974
    /// ```
975
    /// # use bevy_hanabi::*;
976
    /// // Literal expressions always have a constant, build-time value type.
977
    /// let expr = Expr::Literal(LiteralExpr::new(1.));
978
    /// assert_eq!(
979
    ///     expr.value_type(),
980
    ///     Some(ValueType::Scalar(ScalarType::Float))
981
    /// );
982
    /// ```
983
    ///
984
    /// [`eval()`]: crate::graph::Expr::eval
985
    pub fn value_type(&self) -> Option<ValueType> {
25✔
986
        match self {
25✔
987
            Expr::BuiltIn(expr) => Some(expr.value_type()),
×
988
            Expr::Literal(expr) => Some(expr.value_type()),
42✔
989
            Expr::Property(_) => None,
1✔
990
            Expr::Attribute(expr) => Some(expr.value_type()),
6✔
991
            Expr::ParentAttribute(expr) => Some(expr.value_type()),
×
992
            Expr::Unary { .. } => None,
×
993
            Expr::Binary { .. } => None,
×
994
            Expr::Ternary { .. } => None,
×
995
            Expr::Cast(expr) => Some(expr.value_type()),
×
996
            Expr::TextureSample(expr) => Some(expr.value_type()),
×
997
        }
998
    }
999

1000
    /// Evaluate the expression in the given context.
1001
    ///
1002
    /// Evaluate the full expression as part of the given evaluation context,
1003
    /// returning the WGSL string representation of the expression on success.
1004
    ///
1005
    /// The evaluation context is used to resolve some quantities related to the
1006
    /// effect asset, like its properties. It also holds the [`Module`] that the
1007
    /// expression is part of, to allow resolving sub-expressions of operators.
1008
    ///
1009
    /// # Example
1010
    ///
1011
    /// ```
1012
    /// # use bevy_hanabi::*;
1013
    /// let mut module = Module::default();
1014
    /// # let pl = PropertyLayout::empty();
1015
    /// # let pal = ParticleLayout::default();
1016
    /// # let mut context = ShaderWriter::new(ModifierContext::Update, &pl, &pal);
1017
    /// let handle = module.lit(1.);
1018
    /// let expr = module.get(handle).unwrap();
1019
    /// assert_eq!(Ok("1.".to_string()), expr.eval(&module, &mut context));
1020
    /// ```
1021
    pub fn eval(
250✔
1022
        &self,
1023
        module: &Module,
1024
        context: &mut dyn EvalContext,
1025
    ) -> Result<String, ExprError> {
1026
        match self {
250✔
1027
            Expr::BuiltIn(expr) => expr.eval(context),
34✔
1028
            Expr::Literal(expr) => expr.eval(context),
400✔
1029
            Expr::Property(expr) => expr.eval(module, context),
10✔
1030
            Expr::Attribute(expr) => expr.eval(context, false),
120✔
1031
            Expr::ParentAttribute(expr) => expr.eval(context, true),
×
1032
            Expr::Unary { op, expr } => {
76✔
1033
                // Recursively evaluate child expressions throught the context to ensure caching
1034
                let expr = context.eval(module, *expr)?;
190✔
1035

1036
                // if expr.value_type() != self.value_type() {
1037
                //     return Err(ExprError::TypeError(format!(
1038
                //         "Cannot apply normalize() function to non-vector expression: {}",
1039
                //         expr.unwrap_or("(error evaluating expression)".to_string())
1040
                //     )));
1041
                // }
1042

1043
                Ok(if op.is_functional() {
1044
                    format!("{}({})", op.to_wgsl_string(), expr)
136✔
1045
                } else {
1046
                    format!("{}.{}", expr, op.to_wgsl_string())
4✔
1047
                })
1048
            }
1049
            Expr::Binary { op, left, right } => {
117✔
1050
                // Recursively evaluate child expressions throught the context to ensure caching
1051
                let compiled_left = context.eval(module, *left)?;
195✔
1052
                let compiled_right = context.eval(module, *right)?;
39✔
1053

1054
                // if !self.input.value_type().is_vector() {
1055
                //     return Err(ExprError::TypeError(format!(
1056
                //         "Cannot apply normalize() function to non-vector expression: {}",
1057
                //         expr.unwrap_or("(error evaluating expression)".to_string())
1058
                //     )));
1059
                // }
1060

1061
                let body = if op.is_functional() {
39✔
1062
                    if op.needs_type_suffix() {
24✔
1063
                        let lhs_type = module.get(*left).and_then(|arg| arg.value_type());
×
1064
                        let rhs_type = module.get(*right).and_then(|arg| arg.value_type());
×
1065
                        if lhs_type.is_none() || rhs_type.is_none() {
×
1066
                            return Err(ExprError::TypeError(
×
1067
                                "Can't determine the type of the operand".to_string(),
×
1068
                            ));
1069
                        }
1070
                        if lhs_type != rhs_type {
1071
                            return Err(ExprError::TypeError("Mismatched types".to_string()));
×
1072
                        }
1073
                        let value_type = lhs_type.unwrap();
1074
                        let suffix = match value_type {
×
1075
                            ValueType::Scalar(ScalarType::Float) => "f",
×
1076
                            ValueType::Vector(vector_type)
×
1077
                                if vector_type.elem_type() == ScalarType::Float =>
×
1078
                            {
1079
                                match vector_type.count() {
×
1080
                                    2 => "vec2",
×
1081
                                    3 => "vec3",
×
1082
                                    4 => "vec4",
×
1083
                                    _ => unreachable!(),
1084
                                }
1085
                            }
1086
                            _ => {
1087
                                // Add more types here as needed.
1088
                                return Err(ExprError::TypeError("Unsupported type".to_string()));
×
1089
                            }
1090
                        };
1091

1092
                        Ok(format!(
1093
                            "{}_{}({}, {})",
1094
                            op.to_wgsl_string(),
1095
                            suffix,
1096
                            compiled_left,
1097
                            compiled_right
1098
                        ))
1099
                    } else {
1100
                        Ok(format!(
12✔
1101
                            "{}({}, {})",
12✔
1102
                            op.to_wgsl_string(),
12✔
1103
                            compiled_left,
12✔
1104
                            compiled_right
12✔
1105
                        ))
1106
                    }
1107
                } else {
1108
                    Ok(format!(
27✔
1109
                        "({}) {} ({})",
27✔
1110
                        compiled_left,
27✔
1111
                        op.to_wgsl_string(),
27✔
1112
                        compiled_right
27✔
1113
                    ))
1114
                };
1115

1116
                body.map(|body| {
39✔
1117
                    check_side_effects_and_create_local_if_needed(
39✔
1118
                        context,
39✔
1119
                        body,
39✔
1120
                        self.has_side_effect(module),
117✔
1121
                    )
1122
                })
1123
            }
1124
            Expr::Ternary {
1125
                op,
3✔
1126
                first,
3✔
1127
                second,
3✔
1128
                third,
3✔
1129
            } => {
1130
                // Recursively evaluate child expressions throught the context to ensure caching
1131
                let first = context.eval(module, *first)?;
15✔
1132
                let second = context.eval(module, *second)?;
3✔
1133
                let third = context.eval(module, *third)?;
3✔
1134

1135
                // if !self.input.value_type().is_vector() {
1136
                //     return Err(ExprError::TypeError(format!(
1137
                //         "Cannot apply normalize() function to non-vector expression: {}",
1138
                //         expr.unwrap_or("(error evaluating expression)".to_string())
1139
                //     )));
1140
                // }
1141

1142
                Ok(format!(
1143
                    "{}({}, {}, {})",
1144
                    op.to_wgsl_string(),
1145
                    first,
1146
                    second,
1147
                    third
1148
                ))
1149
            }
1150
            Expr::Cast(expr) => {
4✔
1151
                // Recursively evaluate child expressions throught the context to ensure caching
1152
                let inner = context.eval(module, expr.inner)?;
20✔
1153

1154
                Ok(format!("{}({})", expr.target.to_wgsl_string(), inner))
1155
            }
1156
            Expr::TextureSample(expr) => expr.eval(module, context),
×
1157
        }
1158
    }
1159
}
1160

1161
/// A literal constant expression like `3.0` or `vec3<f32>(1.0, 2.0, 3.0)`.
1162
///
1163
/// Literal expression are compile-time constants. They are always constant
1164
/// ([`is_const()`] is `true`) and have a value type equal to the type of the
1165
/// constant itself.
1166
///
1167
/// [`is_const()`]: LiteralExpr::is_const
1168
#[derive(Debug, Clone, Copy, PartialEq, Hash, Reflect, Serialize, Deserialize)]
1169
#[serde(transparent)]
1170
pub struct LiteralExpr {
1171
    value: Value,
1172
}
1173

1174
impl LiteralExpr {
1175
    /// Create a new literal expression from a [`Value`].
1176
    pub fn new<V>(value: V) -> Self
80✔
1177
    where
1178
        Value: From<V>,
1179
    {
1180
        Self {
1181
            value: value.into(),
80✔
1182
        }
1183
    }
1184

1185
    /// Is the expression resulting in a compile-time constant which can be
1186
    /// hard-coded into a shader's code?
1187
    pub fn is_const(&self) -> bool {
×
1188
        true
×
1189
    }
1190

1191
    /// Get the value type of the expression.
1192
    pub fn value_type(&self) -> ValueType {
21✔
1193
        self.value.value_type()
42✔
1194
    }
1195

1196
    /// Evaluate the expression in the given context.
1197
    pub fn eval(&self, _context: &dyn EvalContext) -> Result<String, ExprError> {
100✔
1198
        Ok(self.value.to_wgsl_string())
100✔
1199
    }
1200
}
1201

1202
impl ToWgslString for LiteralExpr {
1203
    fn to_wgsl_string(&self) -> String {
×
1204
        self.value.to_wgsl_string()
×
1205
    }
1206
}
1207

1208
impl From<&Value> for LiteralExpr {
1209
    fn from(value: &Value) -> Self {
×
1210
        Self { value: *value }
×
1211
    }
1212
}
1213

1214
impl<T: Into<Value>> From<T> for LiteralExpr {
1215
    fn from(value: T) -> Self {
×
1216
        Self {
1217
            value: value.into(),
×
1218
        }
1219
    }
1220
}
1221

1222
/// Expression representing the value of an attribute of a particle.
1223
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1224
pub struct AttributeExpr {
1225
    attr: Attribute,
1226
}
1227

1228
impl AttributeExpr {
1229
    /// Create a new attribute expression.
1230
    #[inline]
1231
    pub fn new(attr: Attribute) -> Self {
32✔
1232
        Self { attr }
1233
    }
1234

1235
    /// Is the expression resulting in a compile-time constant which can be
1236
    /// hard-coded into a shader's code?
1237
    pub fn is_const(&self) -> bool {
×
1238
        false
×
1239
    }
1240

1241
    /// Get the value type of the expression.
1242
    pub fn value_type(&self) -> ValueType {
3✔
1243
        self.attr.value_type()
6✔
1244
    }
1245

1246
    /// Evaluate the expression in the given context.
1247
    pub fn eval(&self, context: &dyn EvalContext, parent: bool) -> Result<String, ExprError> {
30✔
1248
        if self.attr == Attribute::ID {
30✔
1249
            // Pseudo-attribute, not stored in the particle buffer
1250
            Ok(if parent {
×
1251
                "parent_particle_index"
×
1252
            } else {
1253
                "particle_index"
×
1254
            }
1255
            .to_string())
×
1256
        } else if self.attr == Attribute::PARTICLE_COUNTER {
30✔
1257
            // Pseudo-attribute, not stored in the particle buffer
1258
            Ok("particle_counter".to_string())
×
1259
        } else {
1260
            let owner = if parent {
30✔
1261
                "parent_particle"
×
1262
            } else {
1263
                "particle"
30✔
1264
            };
1265
            if context.is_attribute_pointer() {
1266
                Ok(format!("(*{}).{}", owner, self.attr.name()))
6✔
1267
            } else {
1268
                Ok(format!("{}.{}", owner, self.attr.name()))
28✔
1269
            }
1270
        }
1271
    }
1272
}
1273

1274
impl ToWgslString for AttributeExpr {
1275
    fn to_wgsl_string(&self) -> String {
×
1276
        format!("particle.{}", self.attr.name())
×
1277
    }
1278
}
1279

1280
impl From<Attribute> for AttributeExpr {
1281
    fn from(value: Attribute) -> Self {
×
1282
        AttributeExpr::new(value)
×
1283
    }
1284
}
1285

1286
/// Expression representing the value of a property of an effect.
1287
///
1288
/// A property expression represents the value of the property at the time the
1289
/// expression appears. In shader, the expression yields a read from the
1290
/// property memory location.
1291
///
1292
/// To create a property to reference with an expression, use
1293
/// [`Module::add_property()`].
1294
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1295
#[repr(transparent)]
1296
#[serde(transparent)]
1297
pub struct PropertyExpr {
1298
    property: PropertyHandle,
1299
}
1300

1301
impl PropertyExpr {
1302
    /// Create a new property expression.
1303
    #[inline]
1304
    pub fn new(property: PropertyHandle) -> Self {
4✔
1305
        Self { property }
1306
    }
1307

1308
    /// Is the expression resulting in a compile-time constant which can be
1309
    /// hard-coded into a shader's code?
1310
    fn is_const(&self) -> bool {
×
1311
        false
×
1312
    }
1313

1314
    /// Evaluate the expression in the given context.
1315
    fn eval(&self, module: &Module, context: &dyn EvalContext) -> Result<String, ExprError> {
2✔
1316
        let prop = module
4✔
1317
            .get_property(self.property)
4✔
1318
            .ok_or(ExprError::PropertyError(format!(
4✔
1319
                "Unknown property handle {:?} in evaluation module.",
2✔
1320
                self.property
2✔
1321
            )))?;
1322
        if !context.property_layout().contains(prop.name()) {
1323
            return Err(ExprError::PropertyError(format!(
×
1324
                "Unknown property '{}' in evaluation layout.",
×
1325
                prop.name()
×
1326
            )));
1327
        }
1328

1329
        Ok(format!("properties.{}", prop.name()))
6✔
1330
    }
1331
}
1332

1333
/// Expression to cast an expression to another type.
1334
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1335
pub struct CastExpr {
1336
    /// The operand expression to cast.
1337
    inner: ExprHandle,
1338
    /// The target type to cast to.
1339
    target: ValueType,
1340
}
1341

1342
impl CastExpr {
1343
    /// Create a new cast expression.
1344
    #[inline]
1345
    pub fn new(inner: ExprHandle, target: impl Into<ValueType>) -> Self {
12✔
1346
        Self {
1347
            inner,
1348
            target: target.into(),
12✔
1349
        }
1350
    }
1351

1352
    /// Get the value type of the expression.
1353
    pub fn value_type(&self) -> ValueType {
3✔
1354
        self.target
3✔
1355
    }
1356

1357
    /// Try to evaluate if the cast expression is valid.
1358
    ///
1359
    /// The evaluation fails if the value type of the operand cannot be
1360
    /// determined. In that case, the function returns `None`.
1361
    ///
1362
    /// Valid cast expressions are:
1363
    /// - scalar to scalar
1364
    /// - scalar to vector
1365
    /// - vector to vector
1366
    /// - matrix to matrix
1367
    pub fn is_valid(&self, module: &Module) -> Option<bool> {
12✔
1368
        let Some(inner) = module.get(self.inner) else {
36✔
1369
            return Some(false);
×
1370
        };
1371
        if let Some(inner_type) = inner.value_type() {
11✔
1372
            match self.target {
1373
                ValueType::Scalar(_) => {
1374
                    // scalar -> scalar only
1375
                    Some(matches!(inner_type, ValueType::Scalar(_)))
8✔
1376
                }
1377
                ValueType::Vector(_) => {
1378
                    // {scalar, vector} -> vector
1379
                    Some(!matches!(inner_type, ValueType::Matrix(_)))
7✔
1380
                }
1381
                ValueType::Matrix(_) => {
1382
                    // matrix -> matrix only
1383
                    Some(matches!(inner_type, ValueType::Matrix(_)))
4✔
1384
                }
1385
            }
1386
        } else {
1387
            None
1✔
1388
        }
1389
    }
1390
}
1391

1392
/// Expression to sample a texture from the effect's material.
1393
///
1394
/// This currently supports only 4-component textures sampled with [the WGSL
1395
/// `textureSample()` function], that is all textures which return a `vec4<f32>`
1396
/// when sampled. This is the case of most color textures, including
1397
/// single-channel (e.g. red) textures which return zero for missing components,
1398
/// but excludes depth textures.
1399
///
1400
/// [the WGSL `textureSample()` function]: https://www.w3.org/TR/WGSL/#texturesample
1401
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1402
pub struct TextureSampleExpr {
1403
    /// The index of the image to sample. This is the texture slot defined in
1404
    /// the [`Module`]. The texture bound to the slot is defined in the
1405
    /// [`EffectMaterial`] component.
1406
    ///
1407
    /// [`EffectMaterial`]: crate::EffectMaterial
1408
    pub image: ExprHandle,
1409
    /// The coordinates to sample at.
1410
    pub coordinates: ExprHandle,
1411
}
1412

1413
impl TextureSampleExpr {
1414
    /// Create a new texture sample expression.
1415
    #[inline]
1416
    pub fn new(image: ExprHandle, coordinates: ExprHandle) -> Self {
×
1417
        Self { image, coordinates }
1418
    }
1419

1420
    /// Get the value type of the expression.
1421
    pub fn value_type(&self) -> ValueType {
×
1422
        // FIXME - depth textures return a single f32 when sampled
1423
        ValueType::Vector(VectorType::VEC4F)
×
1424
    }
1425

1426
    /// Try to evaluate if the texture sample expression is valid.
1427
    ///
1428
    /// This only checks that the expressions exist in the module.
1429
    pub fn is_valid(&self, module: &Module) -> Option<bool> {
×
1430
        let Some(_image) = module.get(self.image) else {
×
1431
            return Some(false);
×
1432
        };
1433
        let Some(_coordinates) = module.get(self.coordinates) else {
×
1434
            return Some(false);
×
1435
        };
1436
        Some(true)
1437
    }
1438

1439
    /// Evaluate the expression in the given context.
1440
    pub fn eval(
×
1441
        &self,
1442
        module: &Module,
1443
        context: &mut dyn EvalContext,
1444
    ) -> Result<String, ExprError> {
1445
        let image = module.try_get(self.image)?;
×
1446
        let image = image.eval(module, context)?;
×
1447
        let coordinates = module.try_get(self.coordinates)?;
×
1448
        let coordinates = coordinates.eval(module, context)?;
×
1449
        Ok(format!(
1450
            "textureSample(material_texture_{image}, material_sampler_{image}, {coordinates})",
1451
        ))
1452
    }
1453
}
1454

1455
/// Built-in operators.
1456
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1457
pub enum BuiltInOperator {
1458
    /// Current effect system simulation time since startup, in seconds.
1459
    ///
1460
    /// This is based on the
1461
    /// [`Time<EffectSimulation>`](crate::time::EffectSimulation) clock.
1462
    ///
1463
    /// Type: `f32`
1464
    Time,
1465
    /// Delta time, in seconds, since last effect system update.
1466
    ///
1467
    /// Type: `f32`
1468
    DeltaTime,
1469
    /// Current virtual time since startup, in seconds.
1470
    ///
1471
    /// This is based on the [`Time<Virtual>`](bevy::time::Virtual) clock.
1472
    ///
1473
    /// Type: `f32`
1474
    VirtualTime,
1475
    /// Virtual delta time, in seconds, since last effect system update.
1476
    ///
1477
    /// Type: `f32`
1478
    VirtualDeltaTime,
1479
    /// Current real time since startup, in seconds.
1480
    ///
1481
    /// This is based on the [`Time<Time>`](bevy::time::Real) clock.
1482
    ///
1483
    /// Type: `f32`
1484
    RealTime,
1485
    /// Real delta time, in seconds, since last effect system update.
1486
    ///
1487
    /// Type: `f32`
1488
    RealDeltaTime,
1489
    /// Random unit value of the given type.
1490
    ///
1491
    /// The type can be any scalar or vector type. Matrix types are not
1492
    /// supported. The random values generated are uniformly distributed in
1493
    /// `[0:1]`. For vectors, each component is sampled separately.
1494
    ///
1495
    /// The random number generator is from [the PCG family] of PRNGs, and is
1496
    /// implemented directly inside the shader, and running on the GPU
1497
    /// exclusively. It's seeded by the [`SpawnerSettings`].
1498
    ///
1499
    /// Type: Same as its [`ValueType`].
1500
    ///
1501
    /// [the PCG family]: https://www.pcg-random.org/
1502
    /// [`SpawnerSettings`]: crate::SpawnerSettings
1503
    Rand(ValueType),
1504
    /// Value of the alpha cutoff for alpha masking.
1505
    ///
1506
    /// This value is only available in the render context. It represents the
1507
    /// current threshold, generally in \[0:1\], which the particle's fragment
1508
    /// alpha value will be compared against to determine alpha masking.
1509
    ///
1510
    /// The value is initalized at the beginning of the fragment shader to the
1511
    /// expression stored in [`AlphaMode::Mask`].
1512
    ///
1513
    /// Type: `f32`
1514
    ///
1515
    /// [`AlphaMode::Mask`]: crate::AlphaMode::Mask
1516
    AlphaCutoff,
1517
    /// Alive flag for the particle.
1518
    ///
1519
    /// The alive flag is initialized at the beginning of the update pass:
1520
    /// - If the particle has both the [`Attribute::AGE`] and
1521
    ///   [`Attribute::LIFETIME`], then the initial value at the beginning of
1522
    ///   the update pass is `age < lifetime`.
1523
    /// - Otherwise, the initial value is `true`.
1524
    ///
1525
    /// At the end of the update pass, if the particle has both the
1526
    /// [`Attribute::AGE`] and [`Attribute::LIFETIME`], then the flag is
1527
    /// re-evaluated as:
1528
    ///
1529
    /// ```wgsl
1530
    /// is_alive = is_alive && (particle.age < particle.lifetime);
1531
    /// ```
1532
    ///
1533
    /// If the flag is `false` after that, the particle is considered dead and
1534
    /// it's deallocated.
1535
    ///
1536
    /// This flag is only available in the update pass. Attempting to use it
1537
    /// inside either the init or render passes will generate an invalid shader.
1538
    ///
1539
    /// Type: `bool`
1540
    IsAlive,
1541
}
1542

1543
impl BuiltInOperator {
1544
    /// Get the operator name.
1545
    pub fn name(&self) -> &str {
49✔
1546
        match self {
49✔
1547
            BuiltInOperator::Time => "time",
6✔
1548
            BuiltInOperator::DeltaTime => "delta_time",
13✔
1549
            BuiltInOperator::VirtualTime => "virtual_time",
2✔
1550
            BuiltInOperator::VirtualDeltaTime => "virtual_delta_time",
2✔
1551
            BuiltInOperator::RealTime => "real_time",
2✔
1552
            BuiltInOperator::RealDeltaTime => "real_delta_time",
2✔
1553
            BuiltInOperator::Rand(value_type) => match value_type {
44✔
1554
                ValueType::Scalar(s) => match s {
20✔
1555
                    ScalarType::Bool => "brand",
1✔
1556
                    ScalarType::Float => "frand",
7✔
1557
                    ScalarType::Int => "irand",
1✔
1558
                    ScalarType::Uint => "urand",
1✔
1559
                },
1560
                ValueType::Vector(vector_type) => {
12✔
1561
                    match (vector_type.elem_type(), vector_type.count()) {
36✔
1562
                        (ScalarType::Bool, 2) => "brand2",
1✔
1563
                        (ScalarType::Bool, 3) => "brand3",
1✔
1564
                        (ScalarType::Bool, 4) => "brand4",
1✔
1565
                        (ScalarType::Float, 2) => "frand2",
1✔
1566
                        (ScalarType::Float, 3) => "frand3",
1✔
1567
                        (ScalarType::Float, 4) => "frand4",
1✔
1568
                        (ScalarType::Int, 2) => "irand2",
1✔
1569
                        (ScalarType::Int, 3) => "irand3",
1✔
1570
                        (ScalarType::Int, 4) => "irand4",
1✔
1571
                        (ScalarType::Uint, 2) => "urand2",
1✔
1572
                        (ScalarType::Uint, 3) => "urand3",
1✔
1573
                        (ScalarType::Uint, 4) => "urand4",
1✔
1574
                        _ => panic!("Invalid vector type {:?}", vector_type),
×
1575
                    }
1576
                }
1577
                ValueType::Matrix(_) => panic!("Invalid BuiltInOperator::Rand(ValueType::Matrix)."),
×
1578
            },
1579
            BuiltInOperator::AlphaCutoff => "alpha_cutoff",
×
1580
            BuiltInOperator::IsAlive => "is_alive",
×
1581
        }
1582
    }
1583

1584
    /// Get the type of the value of a built-in operator.
1585
    pub fn value_type(&self) -> ValueType {
4✔
1586
        match self {
4✔
1587
            BuiltInOperator::Time => ValueType::Scalar(ScalarType::Float),
2✔
1588
            BuiltInOperator::DeltaTime => ValueType::Scalar(ScalarType::Float),
2✔
1589
            BuiltInOperator::VirtualTime => ValueType::Scalar(ScalarType::Float),
×
1590
            BuiltInOperator::VirtualDeltaTime => ValueType::Scalar(ScalarType::Float),
×
1591
            BuiltInOperator::RealTime => ValueType::Scalar(ScalarType::Float),
×
1592
            BuiltInOperator::RealDeltaTime => ValueType::Scalar(ScalarType::Float),
×
1593
            BuiltInOperator::Rand(value_type) => *value_type,
×
1594
            BuiltInOperator::AlphaCutoff => ValueType::Scalar(ScalarType::Float),
×
1595
            BuiltInOperator::IsAlive => ValueType::Scalar(ScalarType::Bool),
×
1596
        }
1597
    }
1598

1599
    // /// Evaluate the result of the operator as an expression.
1600
    // pub fn eval(&self, _context: &dyn EvalContext) -> Result<String, ExprError> {
1601
    //     match self {
1602
    //         BuiltInOperator::Time => Value::Scalar(Scal)
1603
    //     }
1604
    // }
1605
}
1606

1607
impl ToWgslString for BuiltInOperator {
1608
    fn to_wgsl_string(&self) -> String {
37✔
1609
        match self {
37✔
1610
            BuiltInOperator::Rand(_) => format!("{}()", self.name()),
22✔
1611
            BuiltInOperator::IsAlive => "is_alive".to_string(),
2✔
1612
            _ => format!("sim_params.{}", self.name()),
56✔
1613
        }
1614
    }
1615
}
1616

1617
/// Expression for getting built-in quantities related to the effect system.
1618
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1619
pub struct BuiltInExpr {
1620
    operator: BuiltInOperator,
1621
}
1622

1623
impl BuiltInExpr {
1624
    /// Create a new built-in operator expression.
1625
    ///
1626
    /// # Panics
1627
    ///
1628
    /// Panics on invalid [`BuiltInOperator`], like `Rand(MatrixType)`. See
1629
    /// each [`BuiltInOperator`] variant for more details.
1630
    #[inline]
1631
    pub fn new(operator: BuiltInOperator) -> Self {
33✔
1632
        if let BuiltInOperator::Rand(value_type) = operator {
51✔
1633
            assert!(!matches!(value_type, ValueType::Matrix(_)));
18✔
1634
        }
1635
        Self { operator }
1636
    }
1637

1638
    /// Is the expression resulting in a compile-time constant?
1639
    ///
1640
    /// Constant expressions can be hard-coded into a shader's code, making them
1641
    /// more efficient and open to shader compiler optimizing.
1642
    pub fn is_const(&self) -> bool {
×
1643
        false
×
1644
    }
1645

1646
    /// Has the expression any side-effect?
1647
    ///
1648
    /// Expressions with side-effect need to be stored into temporary variables
1649
    /// when the shader code is emitted, so that the side effect is only applied
1650
    /// once when the expression is reused in multiple locations.
1651
    pub fn has_side_effect(&self) -> bool {
37✔
1652
        matches!(self.operator, BuiltInOperator::Rand(_))
52✔
1653
    }
1654

1655
    /// Get the value type of the expression.
1656
    ///
1657
    /// The value type of the expression is the type of the value(s) that an
1658
    /// expression produces.
1659
    pub fn value_type(&self) -> ValueType {
×
1660
        self.operator.value_type()
×
1661
    }
1662

1663
    /// Evaluate the expression in the given context.
1664
    pub fn eval(&self, context: &mut dyn EvalContext) -> Result<String, ExprError> {
37✔
1665
        Ok(check_side_effects_and_create_local_if_needed(
74✔
1666
            context,
74✔
1667
            self.to_wgsl_string(),
111✔
1668
            self.has_side_effect(),
37✔
1669
        ))
1670
    }
1671
}
1672

1673
impl ToWgslString for BuiltInExpr {
1674
    fn to_wgsl_string(&self) -> String {
37✔
1675
        self.operator.to_wgsl_string()
74✔
1676
    }
1677
}
1678

1679
/// Creates a local variable if necessary to avoid evaluating side-effecting
1680
/// expressions multiple times.
1681
///
1682
/// If `has_side_effect` is true, this function creates a local variable within
1683
/// the [`EvalContext`] and stores the `compiled_code` there. Otherwise, it
1684
/// simply returns `compiled_code` unchanged.
1685
fn check_side_effects_and_create_local_if_needed(
76✔
1686
    context: &mut dyn EvalContext,
1687
    compiled_code: String,
1688
    has_side_effect: bool,
1689
) -> String {
1690
    if has_side_effect {
76✔
1691
        let var_name = context.make_local_var();
66✔
1692
        context.push_stmt(&format!("let {} = {};", var_name, compiled_code));
66✔
1693
        var_name
22✔
1694
    } else {
1695
        compiled_code
54✔
1696
    }
1697
}
1698

1699
/// Unary operator.
1700
///
1701
/// Operator applied to a single operand to produce another value. The type of
1702
/// the operand and the result are not necessarily the same. Valid operand types
1703
/// depend on the operator itself.
1704
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1705
pub enum UnaryOperator {
1706
    /// Absolute value operator.
1707
    ///
1708
    /// Return the absolute value of the operand, component-wise for vectors.
1709
    /// Only valid for numeric operands.
1710
    Abs,
1711

1712
    /// Inverse cosine operator.
1713
    ///
1714
    /// Return the inverse cosine of the operand, component-wise for vectors.
1715
    /// Only valid for floating-point operands.
1716
    Acos,
1717

1718
    /// Inverse sine operator.
1719
    ///
1720
    /// Return the inverse sine of the operand, component-wise for vectors.
1721
    /// Only valid for floating-point operands.
1722
    Asin,
1723

1724
    /// Inverse tangent operator.
1725
    ///
1726
    /// Return the inverse tangent of the operand, component-wise for vectors.
1727
    /// Only valid for floating-point operands.
1728
    Atan,
1729

1730
    /// Logical ALL operator for bool vectors.
1731
    ///
1732
    /// Return `true` if all the components of the bool vector operand are
1733
    /// `true`. Invalid for any other type of operand.
1734
    All,
1735

1736
    /// Logical ANY operator for bool vectors.
1737
    ///
1738
    /// Return `true` if any component of the bool vector operand is `true`.
1739
    /// Invalid for any other type of operand.
1740
    Any,
1741

1742
    /// Ceiling operator.
1743
    ///
1744
    /// Return the unique integral number `k` such that `k-1 < x <= k`, where
1745
    /// `x` is the operand which the operator applies to.
1746
    Ceil,
1747

1748
    /// Cosine operator.
1749
    Cos,
1750

1751
    /// Natural exponent operator.
1752
    ///
1753
    /// Return the natural exponentiation of the operand (`e^x`), component-wise
1754
    /// for vectors.
1755
    Exp,
1756

1757
    /// Base-2 exponent operator.
1758
    ///
1759
    /// Return two raised to the power of the operand (`2^x`), component-wise
1760
    /// for vectors.
1761
    Exp2,
1762

1763
    /// Floor operator.
1764
    ///
1765
    /// Return the unique integral number `k` such that `k <= x < k+1`, where
1766
    /// `x` is the operand which the operator applies to.
1767
    Floor,
1768

1769
    /// Fractional part operator.
1770
    ///
1771
    /// Return the fractional part of the operand, which is equal to `x -
1772
    /// floor(x)`, component-wise for vectors.
1773
    Fract,
1774

1775
    /// Inverse square root operator.
1776
    ///
1777
    /// Return the inverse square root of the floating-point operand (`1.0 /
1778
    /// sqrt(x)`), component-wise for vectors.
1779
    InvSqrt,
1780

1781
    /// Length operator.
1782
    ///
1783
    /// Return the length of a floating point scalar or vector. The "length" of
1784
    /// a scalar is taken as its absolute value. The length of a vector is the
1785
    /// Euclidian distance `sqrt(x^2 + ...)` (square root of the sum of the
1786
    /// squared components).
1787
    ///
1788
    /// The output is always a floating point scalar.
1789
    Length,
1790

1791
    /// Natural logarithm operator.
1792
    ///
1793
    /// Return the natural logarithm of the operand (`log(x)`), component-wise
1794
    /// for vectors.
1795
    Log,
1796

1797
    /// Base-2 logarithm operator.
1798
    ///
1799
    /// Return the base-2 logarithm of the operand (`log2(x)`), component-wise
1800
    /// for vectors.
1801
    Log2,
1802

1803
    /// Vector normalizing operator.
1804
    ///
1805
    /// Normalize the given numeric vector. Only valid for numeric vector
1806
    /// operands.
1807
    Normalize,
1808

1809
    /// Packing operator from `vec4<f32>` to `u32` (signed normalized).
1810
    ///
1811
    /// Convert the four components of a signed normalized floating point vector
1812
    /// into a signed integral `i8` value in `[-128:127]`, then pack those
1813
    /// four values into a single `u32`. Each vector component should be in
1814
    /// `[-1:1]` before packing; values outside this range are clamped.
1815
    Pack4x8snorm,
1816

1817
    /// Packing operator from `vec4<f32>` to `u32` (unsigned normalized).
1818
    ///
1819
    /// Convert the four components of an unsigned normalized floating point
1820
    /// vector into an unsigned integral `u8` value in `[0:255]`, then pack
1821
    /// those four values into a single `u32`. Each vector component should
1822
    /// be in `[0:1]` before packing; values outside this range are clamped.
1823
    Pack4x8unorm,
1824

1825
    /// Rounding operator.
1826
    ///
1827
    /// Round the given scalar of vector float to the nearest integer, returned
1828
    /// as a float value.
1829
    Round,
1830

1831
    /// Saturate operator.
1832
    ///
1833
    /// Clamp the value of the operand to the \[0:1\] range, component-wise for
1834
    /// vectors.
1835
    Saturate,
1836

1837
    /// Sign operator.
1838
    ///
1839
    /// Return a value representing the sign of a floating point scalar or
1840
    /// vector input:
1841
    /// - `1.` if the operand is > 0
1842
    /// - `0.` if the operand is = 0
1843
    /// - `-1.` if the operand is < 0
1844
    ///
1845
    /// Applies component-wise for vectors.
1846
    Sign,
1847

1848
    /// Sine operator.
1849
    Sin,
1850

1851
    /// Square root operator.
1852
    ///
1853
    /// Return the square root of the floating-point operand, component-wise for
1854
    /// vectors.
1855
    Sqrt,
1856

1857
    /// Tangent operator.
1858
    Tan,
1859

1860
    /// Unpacking operator from `u32` to `vec4<f32>` (signed normalized).
1861
    ///
1862
    /// Unpack the `u32` into four signed integral `i8` value in `[-128:127]`,
1863
    /// then convert each value to a signed normalized `f32` value in `[-1:1]`.
1864
    Unpack4x8snorm,
1865

1866
    /// Unpacking operator from `u32` to `vec4<f32>` (unsigned normalized).
1867
    ///
1868
    /// Unpack the `u32` into four unsigned integral `u8` value in `[0:255]`,
1869
    /// then convert each value to an unsigned normalized `f32` value in
1870
    /// `[0:1]`.
1871
    Unpack4x8unorm,
1872

1873
    /// Get the fourth component of a vector.
1874
    ///
1875
    /// This is only valid for vectors of rank 4.
1876
    W,
1877

1878
    /// Get the first component of a scalar or vector.
1879
    ///
1880
    /// For scalar, return the value itself. For vectors, return the first
1881
    /// component.
1882
    X,
1883

1884
    /// Get the second component of a vector.
1885
    Y,
1886

1887
    /// Get the third component of a vector.
1888
    ///
1889
    /// This is only valid for vectors of rank 3 or more.
1890
    Z,
1891
}
1892

1893
impl UnaryOperator {
1894
    /// Check if a unary operator is called via a functional-style call.
1895
    ///
1896
    /// Functional-style calls are in the form `op(inner)`, like `abs(x)` for
1897
    /// example, while non-functional ones are in the form `inner.op`,
1898
    /// like `v.x` for example. This check is used for formatting the WGSL
1899
    /// code emitted during evaluation of a binary operation expression.
1900
    pub fn is_functional(&self) -> bool {
38✔
1901
        !matches!(
34✔
1902
            *self,
38✔
1903
            UnaryOperator::X | UnaryOperator::Y | UnaryOperator::Z | UnaryOperator::W
1904
        )
1905
    }
1906
}
1907

1908
impl ToWgslString for UnaryOperator {
1909
    fn to_wgsl_string(&self) -> String {
38✔
1910
        match *self {
38✔
1911
            UnaryOperator::Abs => "abs".to_string(),
5✔
1912
            UnaryOperator::Acos => "acos".to_string(),
2✔
1913
            UnaryOperator::Asin => "asin".to_string(),
2✔
1914
            UnaryOperator::Atan => "atan".to_string(),
2✔
1915
            UnaryOperator::All => "all".to_string(),
2✔
1916
            UnaryOperator::Any => "any".to_string(),
6✔
1917
            UnaryOperator::Ceil => "ceil".to_string(),
2✔
1918
            UnaryOperator::Cos => "cos".to_string(),
2✔
1919
            UnaryOperator::Exp => "exp".to_string(),
2✔
1920
            UnaryOperator::Exp2 => "exp2".to_string(),
2✔
1921
            UnaryOperator::Floor => "floor".to_string(),
2✔
1922
            UnaryOperator::Fract => "fract".to_string(),
2✔
1923
            UnaryOperator::InvSqrt => "inverseSqrt".to_string(),
2✔
1924
            UnaryOperator::Length => "length".to_string(),
2✔
1925
            UnaryOperator::Log => "log".to_string(),
2✔
1926
            UnaryOperator::Log2 => "log2".to_string(),
2✔
1927
            UnaryOperator::Normalize => "normalize".to_string(),
4✔
1928
            UnaryOperator::Pack4x8snorm => "pack4x8snorm".to_string(),
2✔
1929
            UnaryOperator::Pack4x8unorm => "pack4x8unorm".to_string(),
2✔
1930
            UnaryOperator::Round => "round".to_string(),
2✔
1931
            UnaryOperator::Saturate => "saturate".to_string(),
2✔
1932
            UnaryOperator::Sign => "sign".to_string(),
2✔
1933
            UnaryOperator::Sin => "sin".to_string(),
2✔
1934
            UnaryOperator::Sqrt => "sqrt".to_string(),
2✔
1935
            UnaryOperator::Tan => "tan".to_string(),
2✔
1936
            UnaryOperator::Unpack4x8snorm => "unpack4x8snorm".to_string(),
2✔
1937
            UnaryOperator::Unpack4x8unorm => "unpack4x8unorm".to_string(),
2✔
1938
            UnaryOperator::W => "w".to_string(),
2✔
1939
            UnaryOperator::X => "x".to_string(),
2✔
1940
            UnaryOperator::Y => "y".to_string(),
2✔
1941
            UnaryOperator::Z => "z".to_string(),
2✔
1942
        }
1943
    }
1944
}
1945

1946
/// Binary operator.
1947
///
1948
/// Operator applied between two operands, generally denoted "left" and "right".
1949
/// The type of the operands and the result are not necessarily the same. Valid
1950
/// operand types depend on the operator itself.
1951
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
1952
pub enum BinaryOperator {
1953
    /// Addition operator.
1954
    ///
1955
    /// Returns the sum of its operands. Only valid for numeric operands.
1956
    Add,
1957

1958
    /// Inverse tangent operator with 2 arguments.
1959
    ///
1960
    /// Returns the arctangent (inverse tangent) of the ratio of the first
1961
    /// argument divided by the second argument.
1962
    Atan2,
1963

1964
    /// Cross product operator.
1965
    ///
1966
    /// Returns the cross product of the left and right operands. Only valid for
1967
    /// vector type operands of size 3. Always produce a vector result of the
1968
    /// same size.
1969
    Cross,
1970

1971
    /// Distance operator.
1972
    ///
1973
    /// Returns the distance between two floating point scalar or vectors, that
1974
    /// is `length(right - left)`.
1975
    Distance,
1976

1977
    /// Division operator.
1978
    ///
1979
    /// Returns the left operand divided by the right operand. Only valid for
1980
    /// numeric operands.
1981
    Div,
1982

1983
    /// Dot product operator.
1984
    ///
1985
    /// Returns the dot product of the left and right operands. Only valid for
1986
    /// vector type operands. Always produce a scalar floating-point result.
1987
    Dot,
1988

1989
    /// Greater-than operator.
1990
    ///
1991
    /// Returns `true` if the left operand is strictly greater than the right
1992
    /// operand. Only valid for numeric types. If the operands are vectors,
1993
    /// they must be of the same rank, and the result is a bool vector of
1994
    /// that rank.
1995
    GreaterThan,
1996

1997
    /// Greater-than-or-equal operator.
1998
    ///
1999
    /// Returns `true` if the left operand is greater than or equal to the right
2000
    /// operand. Only valid for numeric types. If the operands are vectors,
2001
    /// they must be of the same rank, and the result is a bool vector of
2002
    /// that rank.
2003
    GreaterThanOrEqual,
2004

2005
    /// Less-than operator.
2006
    ///
2007
    /// Returns `true` if the left operand is strictly less than the right
2008
    /// operand. Only valid for numeric types. If the operands are vectors,
2009
    /// they must be of the same rank, and the result is a bool vector of
2010
    /// that rank.
2011
    LessThan,
2012

2013
    /// Less-than-or-equal operator.
2014
    ///
2015
    /// Returns `true` if the left operand is less than or equal to the right
2016
    /// operand. Only valid for numeric types. If the operands are vectors,
2017
    /// they must be of the same rank, and the result is a bool vector of
2018
    /// that rank.
2019
    LessThanOrEqual,
2020

2021
    /// Maximum operator.
2022
    ///
2023
    /// Returns the maximum value of its left and right operands. Only valid for
2024
    /// numeric types. If the operands are vectors, they must be of the same
2025
    /// rank, and the result is a vector of that rank and same element
2026
    /// scalar type.
2027
    Max,
2028

2029
    /// Minimum operator.
2030
    ///
2031
    /// Returns the minimum value of its left and right operands. Only valid for
2032
    /// numeric types. If the operands are vectors, they must be of the same
2033
    /// rank, and the result is a vector of that rank and same element
2034
    /// scalar type.
2035
    Min,
2036

2037
    /// Multiply operator.
2038
    ///
2039
    /// Returns the product of its operands. Only valid for numeric operands.
2040
    Mul,
2041

2042
    /// Remainder operator.
2043
    ///
2044
    /// Returns the remainder of the division of the first operand by the
2045
    /// second. Only valid for numeric types. If the operands are vectors,
2046
    /// they must be of the same rank, and the result is a vector of that
2047
    /// rank and same element scalar type.
2048
    Remainder,
2049

2050
    /// Stepping operator.
2051
    ///
2052
    /// Returns `1.0` if the left operand is less than or equal to the right
2053
    /// operand, or `0.0` otherwise. Only valid for floating scalar or vectors
2054
    /// of the same rank, and applied component-wise for vectors.
2055
    Step,
2056

2057
    /// Subtraction operator.
2058
    ///
2059
    /// Returns the difference between its left and right operands. Only valid
2060
    /// for numeric operands.
2061
    Sub,
2062

2063
    /// Uniform random number operator.
2064
    ///
2065
    /// Returns a value generated by a fast non-cryptographically-secure
2066
    /// pseudo-random number generator (PRNG) whose statistical characteristics
2067
    /// are undefined and generally focused around speed. The random value is
2068
    /// uniformly distributed between the left and right operands, which must be
2069
    /// numeric types. If the operands are vectors, they must be of the same
2070
    /// rank, and the result is a vector of that rank and same element
2071
    /// scalar type.
2072
    UniformRand,
2073

2074
    /// Normal distribution random number operator.
2075
    ///
2076
    /// Returns a value generated by a fast non-cryptographically-secure
2077
    /// pseudo-random number generator (PRNG) whose statistical characteristics
2078
    /// are undefined and generally focused around speed. The random value is
2079
    /// normally distributed with mean given by the first operand and standard
2080
    /// deviation by the second, which must be numeric types. If the operands
2081
    /// are vectors, they must be of the same rank, and the result is a vector
2082
    /// of that rank and same element scalar type.
2083
    NormalRand,
2084

2085
    /// Constructor for 2-element vectors.
2086
    ///
2087
    /// Given two scalar elements `x` and `y`, returns the vector consisting of
2088
    /// those two elements `(x, y)`.
2089
    Vec2,
2090

2091
    /// Constructor for a 4-element vector.
2092
    ///
2093
    /// Given a 3-element vector `xyz` and a scalar value `w`, returns the
2094
    /// vector `vec4(xyz, w)`.
2095
    Vec4XyzW,
2096
}
2097

2098
impl BinaryOperator {
2099
    /// Check if a binary operator is called via a functional-style call.
2100
    ///
2101
    /// Functional-style calls are in the form `op(lhs, rhs)`, like `min(a,
2102
    /// b)` for example, while non-functional ones are in the form `lhs op rhs`,
2103
    /// like `a + b` for example. This check is used for formatting the WGSL
2104
    /// code emitted during evaluation of a binary operation expression.
2105
    pub fn is_functional(&self) -> bool {
39✔
2106
        match *self {
39✔
2107
            BinaryOperator::Add
2108
            | BinaryOperator::Div
2109
            | BinaryOperator::GreaterThan
2110
            | BinaryOperator::GreaterThanOrEqual
2111
            | BinaryOperator::LessThan
2112
            | BinaryOperator::LessThanOrEqual
2113
            | BinaryOperator::Mul
2114
            | BinaryOperator::Remainder
2115
            | BinaryOperator::Sub => false,
27✔
2116
            BinaryOperator::Atan2
2117
            | BinaryOperator::Cross
2118
            | BinaryOperator::Distance
2119
            | BinaryOperator::Dot
2120
            | BinaryOperator::Max
2121
            | BinaryOperator::Min
2122
            | BinaryOperator::Step
2123
            | BinaryOperator::UniformRand
2124
            | BinaryOperator::NormalRand
2125
            | BinaryOperator::Vec2
2126
            | BinaryOperator::Vec4XyzW => true,
12✔
2127
        }
2128
    }
2129

2130
    /// Check if a binary operator needs a type suffix.
2131
    ///
2132
    /// This is currently just for `rand_uniform`
2133
    /// (`BinaryOperator::UniformRand`) and `rand_normal`
2134
    /// (`BinaryOperator::NormalRand`), which are functions we define ourselves.
2135
    /// WGSL doesn't support user-defined function overloading, so we need a
2136
    /// suffix to disambiguate the types.
2137
    pub fn needs_type_suffix(&self) -> bool {
12✔
2138
        matches!(
12✔
2139
            *self,
12✔
2140
            BinaryOperator::UniformRand | BinaryOperator::NormalRand
2141
        )
2142
    }
2143
}
2144

2145
impl ToWgslString for BinaryOperator {
2146
    fn to_wgsl_string(&self) -> String {
39✔
2147
        match *self {
39✔
2148
            BinaryOperator::Add => "+".to_string(),
5✔
2149
            BinaryOperator::Atan2 => "atan2".to_string(),
2✔
2150
            BinaryOperator::Cross => "cross".to_string(),
2✔
2151
            BinaryOperator::Distance => "distance".to_string(),
2✔
2152
            BinaryOperator::Div => "/".to_string(),
4✔
2153
            BinaryOperator::Dot => "dot".to_string(),
2✔
2154
            BinaryOperator::GreaterThan => ">".to_string(),
6✔
2155
            BinaryOperator::GreaterThanOrEqual => ">=".to_string(),
2✔
2156
            BinaryOperator::LessThan => "<".to_string(),
2✔
2157
            BinaryOperator::LessThanOrEqual => "<=".to_string(),
2✔
2158
            BinaryOperator::Max => "max".to_string(),
10✔
2159
            BinaryOperator::Min => "min".to_string(),
4✔
2160
            BinaryOperator::Mul => "*".to_string(),
12✔
2161
            BinaryOperator::Remainder => "%".to_string(),
2✔
2162
            BinaryOperator::Step => "step".to_string(),
2✔
2163
            BinaryOperator::Sub => "-".to_string(),
14✔
2164
            BinaryOperator::UniformRand => "rand_uniform".to_string(),
×
2165
            BinaryOperator::NormalRand => "rand_normal".to_string(),
×
2166
            BinaryOperator::Vec2 => "vec2".to_string(),
×
2167
            BinaryOperator::Vec4XyzW => "vec4".to_string(),
×
2168
        }
2169
    }
2170
}
2171

2172
/// Ternary operator.
2173
///
2174
/// Operator applied between three operands. The type of the operands and the
2175
/// result are not necessarily the same. Valid operand types depend on the
2176
/// operator itself.
2177
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
2178
pub enum TernaryOperator {
2179
    /// Linear blend ("mix") operator.
2180
    ///
2181
    /// Returns the linear blend between the first and second argument, based on
2182
    /// the fraction of the third argument. If the operands are vectors, they
2183
    /// must be of the same rank, and the result is a vector of that rank
2184
    /// and same element scalar type.
2185
    ///
2186
    /// The linear blend of `x` and `y` with fraction `t` is equivalent to `x *
2187
    /// (1 - t) + y * t`.
2188
    Mix,
2189

2190
    /// Smooth stepping operator.
2191
    ///
2192
    /// Returns the smooth Hermitian interpolation between the first and second
2193
    /// argument, calculated at the third argument. If the operands are vectors,
2194
    /// they must be of the same rank, and the result is a vector of that
2195
    /// rank and same element scalar type.
2196
    ///
2197
    /// The smooth stepping of `low` and `high` at position `x` is equivalent to
2198
    /// `t * t * (3. - 2. * t)` where `t = clamp((x - low) / (high - low))`
2199
    /// represents the fractional position of `x` between `low` and `high`.
2200
    ///
2201
    /// The result is always a floating point scalar in \[0:1\].
2202
    SmoothStep,
2203

2204
    /// Constructor for 3-element vectors.
2205
    ///
2206
    /// Given three scalar elements `x`, `y`, and `z`, returns the vector
2207
    /// consisting of those three elements `(x, y, z)`.
2208
    Vec3,
2209
}
2210

2211
impl ToWgslString for TernaryOperator {
2212
    fn to_wgsl_string(&self) -> String {
3✔
2213
        match *self {
3✔
2214
            TernaryOperator::Mix => "mix".to_string(),
2✔
2215
            TernaryOperator::SmoothStep => "smoothstep".to_string(),
2✔
2216
            TernaryOperator::Vec3 => "vec3".to_string(),
×
2217
        }
2218
    }
2219
}
2220

2221
/// Expression writer.
2222
///
2223
/// Utility to write expressions with a simple functional syntax. Expressions
2224
/// created with the writer are gatherned into a [`Module`] which can be
2225
/// transfered once [`finish()`]ed to initialize an [`EffectAsset`].
2226
///
2227
/// Because an [`EffectAsset`] contains a single [`Module`], you generally want
2228
/// to keep using the same [`ExprWriter`] to write all the expressions used by
2229
/// all the [`Modifer`]s assigned to a given [`EffectAsset`], and only then once
2230
/// done call [`finish()`] to recover the [`ExprWriter`]'s underlying [`Module`]
2231
/// to assign it to the [`EffectAsset`].
2232
///
2233
/// # Example
2234
///
2235
/// ```
2236
/// # use bevy_hanabi::*;
2237
/// # use bevy::prelude::*;
2238
/// // Create a writer
2239
/// let w = ExprWriter::new();
2240
///
2241
/// // Create a new expression: max(5. + particle.position, properties.my_prop)
2242
/// let prop = w.add_property("my_property", Vec3::ONE.into());
2243
/// let expr = (w.lit(5.) + w.attr(Attribute::POSITION)).max(w.prop(prop));
2244
///
2245
/// // Finalize the expression and write it down into the `Module` as an `Expr`
2246
/// let expr: ExprHandle = expr.expr();
2247
///
2248
/// // Create a modifier and assign the expression to one of its input(s)
2249
/// let init_modifier = SetAttributeModifier::new(Attribute::LIFETIME, expr);
2250
///
2251
/// // Create an EffectAsset with the modifier and the Module from the writer
2252
/// let effect = EffectAsset::new(1024, SpawnerSettings::rate(32_f32.into()), w.finish())
2253
///     .init(init_modifier);
2254
/// ```
2255
///
2256
/// [`finish()`]: ExprWriter::finish
2257
/// [`EffectAsset`]: crate::EffectAsset
2258
/// [`Modifer`]: crate::Modifier
2259
#[derive(Debug, Default, Clone)]
2260
pub struct ExprWriter {
2261
    module: Rc<RefCell<Module>>,
2262
}
2263

2264
#[allow(dead_code)]
2265
impl ExprWriter {
2266
    /// Create a new writer.
2267
    ///
2268
    /// The writer owns a new [`Module`] internally, and write all expressions
2269
    /// to it. The module can be released to the user with [`finish()`] once
2270
    /// done using the writer.
2271
    ///
2272
    /// [`finish()`]: ExprWriter::finish
2273
    pub fn new() -> Self {
3✔
2274
        Self {
2275
            module: Rc::new(RefCell::new(Module::default())),
6✔
2276
        }
2277
    }
2278

2279
    /// Create a new writer from an existing module.
2280
    ///
2281
    /// This is an advanced use entry point to write expressions into an
2282
    /// existing [`Module`]. In general, users should prefer using
2283
    /// [`ExprWriter::new()`] to create a new [`Module`], and keep using the
2284
    /// same [`ExprWriter`] to write all expressions of the same
2285
    /// [`EffectAsset`].
2286
    ///
2287
    /// [`EffectAsset`]: crate::EffectAsset
2288
    pub fn from_module(module: Rc<RefCell<Module>>) -> Self {
×
2289
        Self { module }
2290
    }
2291

2292
    /// Add a new property.
2293
    ///
2294
    /// See [`Property`] for more details on what effect properties are.
2295
    ///
2296
    /// # Panics
2297
    ///
2298
    /// Panics if a property with the same name already exists.
2299
    pub fn add_property(&self, name: impl Into<String>, default_value: Value) -> PropertyHandle {
1✔
2300
        self.module.borrow_mut().add_property(name, default_value)
3✔
2301
    }
2302

2303
    /// Push a new expression into the writer.
2304
    pub fn push(&self, expr: impl Into<Expr>) -> WriterExpr {
13✔
2305
        let expr = {
13✔
2306
            let mut m = self.module.borrow_mut();
26✔
2307
            m.push(expr.into())
39✔
2308
        };
2309
        WriterExpr {
2310
            expr,
2311
            module: Rc::clone(&self.module),
13✔
2312
        }
2313
    }
2314

2315
    /// Create a new writer expression from a literal constant.
2316
    ///
2317
    /// # Example
2318
    ///
2319
    /// ```
2320
    /// # use bevy_hanabi::*;
2321
    /// let mut w = ExprWriter::new();
2322
    /// let x = w.lit(-3.5); // x = -3.5;
2323
    /// ```
2324
    pub fn lit(&self, value: impl Into<Value>) -> WriterExpr {
11✔
2325
        self.push(Expr::Literal(LiteralExpr {
33✔
2326
            value: value.into(),
11✔
2327
        }))
2328
    }
2329

2330
    /// Create a new writer expression from an attribute.
2331
    ///
2332
    /// # Example
2333
    ///
2334
    /// ```
2335
    /// # use bevy_hanabi::*;
2336
    /// let mut w = ExprWriter::new();
2337
    /// let x = w.attr(Attribute::POSITION); // x = particle.position;
2338
    /// ```
2339
    pub fn attr(&self, attr: Attribute) -> WriterExpr {
1✔
2340
        self.push(Expr::Attribute(AttributeExpr::new(attr)))
3✔
2341
    }
2342

2343
    /// Create a new writer expression from an attribute on a parent effect.
2344
    ///
2345
    /// # Example
2346
    ///
2347
    /// ```
2348
    /// # use bevy_hanabi::*;
2349
    /// let mut w = ExprWriter::new();
2350
    /// let x = w.parent_attr(Attribute::POSITION); // x = parent_particle.position;
2351
    /// ```
2352
    pub fn parent_attr(&self, attr: Attribute) -> WriterExpr {
×
2353
        self.push(Expr::ParentAttribute(AttributeExpr::new(attr)))
×
2354
    }
2355

2356
    /// Create a new writer expression from a property.
2357
    ///
2358
    /// # Example
2359
    ///
2360
    /// ```
2361
    /// # use bevy_hanabi::*;
2362
    /// let mut w = ExprWriter::new();
2363
    /// let prop = w.add_property("my_prop", 3.0.into());
2364
    /// let x = w.prop(prop); // x = properties.my_prop;
2365
    /// ```
2366
    pub fn prop(&self, handle: PropertyHandle) -> WriterExpr {
1✔
2367
        self.push(Expr::Property(PropertyExpr::new(handle)))
3✔
2368
    }
2369

2370
    /// Create a new writer expression representing the current simulation time.
2371
    ///
2372
    /// # Example
2373
    ///
2374
    /// ```
2375
    /// # use bevy_hanabi::*;
2376
    /// let mut w = ExprWriter::new();
2377
    /// let x = w.time(); // x = sim_params.time;
2378
    /// ```
2379
    pub fn time(&self) -> WriterExpr {
×
2380
        self.push(Expr::BuiltIn(BuiltInExpr::new(BuiltInOperator::Time)))
×
2381
    }
2382

2383
    /// Create a new writer expression representing the simulation delta time
2384
    /// since last frame.
2385
    ///
2386
    /// # Example
2387
    ///
2388
    /// ```
2389
    /// # use bevy_hanabi::*;
2390
    /// let mut w = ExprWriter::new();
2391
    /// let x = w.delta_time(); // x = sim_params.delta_time;
2392
    /// ```
2393
    pub fn delta_time(&self) -> WriterExpr {
×
2394
        self.push(Expr::BuiltIn(BuiltInExpr::new(BuiltInOperator::DeltaTime)))
×
2395
    }
2396

2397
    /// Create a new writer expression representing a random value of the given
2398
    /// type.
2399
    ///
2400
    /// The type can be any scalar or vector type. Matrix types are not
2401
    /// supported. The random values generated are uniformly distributed in
2402
    /// `[0:1]`. For vectors, each component is sampled separately.
2403
    ///
2404
    /// # Panics
2405
    ///
2406
    /// Panics in the same cases as [`BuiltInExpr::new()`] does.
2407
    ///
2408
    /// # Example
2409
    ///
2410
    /// ```
2411
    /// # use bevy_hanabi::*;
2412
    /// let mut w = ExprWriter::new();
2413
    /// let x = w.rand(VectorType::VEC3F); // x = frand3();
2414
    /// ```
2415
    pub fn rand(&self, value_type: impl Into<ValueType>) -> WriterExpr {
×
2416
        self.push(Expr::BuiltIn(BuiltInExpr::new(BuiltInOperator::Rand(
×
2417
            value_type.into(),
×
2418
        ))))
2419
    }
2420

2421
    /// Create a new writer expression representing the alpha cutoff value used
2422
    /// for alpha masking.
2423
    ///
2424
    /// This expression is only valid when used in the context of the fragment
2425
    /// shader, in the render context.
2426
    ///
2427
    /// # Example
2428
    ///
2429
    /// ```
2430
    /// # use bevy_hanabi::*;
2431
    /// let mut w = ExprWriter::new();
2432
    /// let x = w.alpha_cutoff(); // x = alpha_cutoff;
2433
    /// ```
2434
    pub fn alpha_cutoff(&self) -> WriterExpr {
×
2435
        self.push(Expr::BuiltIn(BuiltInExpr::new(
×
2436
            BuiltInOperator::AlphaCutoff,
×
2437
        )))
2438
    }
2439

2440
    /// Finish using the writer, and recover the [`Module`] where all [`Expr`]
2441
    /// were written by the writer.
2442
    ///
2443
    /// This module is typically passed to [`EffectAsset::new()`] before adding
2444
    /// to that effect the modifiers which use the expressions created by this
2445
    /// writer.
2446
    ///
2447
    /// # Example
2448
    ///
2449
    /// ```
2450
    /// # use bevy_hanabi::*;
2451
    /// # let spawner = SpawnerSettings::default();
2452
    /// let mut w = ExprWriter::new();
2453
    /// // [...]
2454
    /// let module = w.finish();
2455
    /// let asset = EffectAsset::new(256, spawner, module);
2456
    /// ```
2457
    ///
2458
    /// [`EffectAsset::new()`]: crate::EffectAsset::new()
2459
    pub fn finish(self) -> Module {
3✔
2460
        self.module.take()
3✔
2461
    }
2462
}
2463

2464
/// Intermediate expression from an [`ExprWriter`].
2465
///
2466
/// A writer expression [`WriterExpr`] is equivalent to an [`ExprHandle`], but
2467
/// retains a reference to the underlying [`Module`] and therefore can easily be
2468
/// chained with other [`WriterExpr`] via a concise syntax, at the expense of
2469
/// being more heavyweight and locking the underlying [`Module`] under a
2470
/// ref-counted interior mutability (`Rc<RefCell<Module>>`). [`ExprHandle`] by
2471
/// opposition is a very lightweight type, similar to a simple index. And like
2472
/// an array index, [`ExprHandle`] doesn't explicitly reference its associated
2473
/// storage ([`Module`]) which needs to be remembered by the user explicitly.
2474
///
2475
/// In addition, [`WriterExpr`] implements several numerical operators like the
2476
/// [`std::ops::Add`] trait, making it simpler to combine it with another
2477
/// [`WriterExpr`].
2478
///
2479
/// ```
2480
/// # use bevy_hanabi::*;
2481
/// let mut w = ExprWriter::new();
2482
/// let x = w.lit(-3.5);
2483
/// let y = w.lit(78.);
2484
/// let z = x + y; // == 74.5
2485
/// ```
2486
///
2487
/// In general the [`WriterExpr`] type is not used directly, but inferred from
2488
/// calling [`ExprWriter`] methods and combining [`WriterExpr`] together.
2489
///
2490
/// ```
2491
/// # use bevy_hanabi::*;
2492
/// let mut w = ExprWriter::new();
2493
/// let my_prop = w.add_property("my_prop", 3.0.into());
2494
///
2495
/// // x = max(-3.5 + 1., properties.my_prop) * 0.5 - particle.position;
2496
/// let x = (w.lit(-3.5) + w.lit(1.))
2497
///     .max(w.prop(my_prop))
2498
///     .mul(w.lit(0.5))
2499
///     .sub(w.attr(Attribute::POSITION));
2500
///
2501
/// let handle: ExprHandle = x.expr();
2502
/// ```
2503
#[derive(Debug, Clone)]
2504
pub struct WriterExpr {
2505
    expr: ExprHandle,
2506
    module: Rc<RefCell<Module>>,
2507
}
2508

2509
impl WriterExpr {
2510
    fn unary_op(self, op: UnaryOperator) -> Self {
1✔
2511
        let expr = self.module.borrow_mut().push(Expr::Unary {
4✔
2512
            op,
1✔
2513
            expr: self.expr,
1✔
2514
        });
2515
        WriterExpr {
2516
            expr,
2517
            module: self.module,
1✔
2518
        }
2519
    }
2520

2521
    /// Take the absolute value of the current expression.
2522
    ///
2523
    /// This is a unary operator, which applies component-wise to vector and
2524
    /// matrix operand expressions.
2525
    ///
2526
    /// # Example
2527
    ///
2528
    /// ```
2529
    /// # use bevy_hanabi::*;
2530
    /// # let mut w = ExprWriter::new();
2531
    /// // A literal expression `x = -3.5;`.
2532
    /// let x = w.lit(-3.5);
2533
    ///
2534
    /// // The absolute value `y = abs(x);`.
2535
    /// let y = x.abs(); // == 3.5
2536
    /// ```
2537
    #[inline]
2538
    pub fn abs(self) -> Self {
1✔
2539
        self.unary_op(UnaryOperator::Abs)
3✔
2540
    }
2541

2542
    /// Apply the logical operator "all" to the current bool vector expression.
2543
    ///
2544
    /// This is a unary operator, which applies to vector operand expressions to
2545
    /// produce a scalar boolean.
2546
    ///
2547
    /// # Example
2548
    ///
2549
    /// ```
2550
    /// # use bevy_hanabi::*;
2551
    /// # use bevy::math::BVec3;
2552
    /// # let mut w = ExprWriter::new();
2553
    /// // A literal expression `x = vec3<bool>(true, false, true);`.
2554
    /// let x = w.lit(BVec3::new(true, false, true));
2555
    ///
2556
    /// // Check if all components are true `y = all(x);`.
2557
    /// let y = x.all(); // == false
2558
    /// ```
2559
    #[inline]
2560
    pub fn all(self) -> Self {
×
2561
        self.unary_op(UnaryOperator::All)
×
2562
    }
2563

2564
    /// Apply the logical operator "any" to the current bool vector expression.
2565
    ///
2566
    /// This is a unary operator, which applies to vector operand expressions to
2567
    /// produce a scalar boolean.
2568
    ///
2569
    /// # Example
2570
    ///
2571
    /// ```
2572
    /// # use bevy_hanabi::*;
2573
    /// # use bevy::math::BVec3;
2574
    /// # let mut w = ExprWriter::new();
2575
    /// // A literal expression `x = vec3<bool>(true, false, true);`.
2576
    /// let x = w.lit(BVec3::new(true, false, true));
2577
    ///
2578
    /// // Check if any components is true `y = any(x);`.
2579
    /// let y = x.any(); // == true
2580
    /// ```
2581
    #[inline]
2582
    pub fn any(self) -> Self {
×
2583
        self.unary_op(UnaryOperator::Any)
×
2584
    }
2585

2586
    /// Apply the "acos" (inverse cosine) operator to the current float scalar
2587
    /// or vector expression.
2588
    ///
2589
    /// This is a unary operator, which applies to float scalar or vector
2590
    /// operand expressions to produce a float scalar or vector. It applies
2591
    /// component-wise to vector operand expressions. The return value lies in
2592
    /// the 0 ≤ x ≤ π range.
2593
    ///
2594
    /// # Example
2595
    ///
2596
    /// ```
2597
    /// # use bevy_hanabi::*;
2598
    /// # use bevy::math::Vec3;
2599
    /// # let mut w = ExprWriter::new();
2600
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2601
    /// let x = w.lit(Vec3::ONE);
2602
    ///
2603
    /// // Acos: `y = acos(x);`
2604
    /// let y = x.acos();
2605
    /// ```
2606
    #[inline]
2607
    pub fn acos(self) -> Self {
×
2608
        self.unary_op(UnaryOperator::Acos)
×
2609
    }
2610

2611
    /// Apply the "asin" (inverse sine) operator to the current float scalar or
2612
    /// vector expression.
2613
    ///
2614
    /// This is a unary operator, which applies to float scalar or vector
2615
    /// operand expressions to produce a float scalar or vector. It applies
2616
    /// component-wise to vector operand expressions. The return value lies in
2617
    /// the -π/2 ≤ x ≤ π/2 range.
2618
    ///
2619
    /// # Example
2620
    ///
2621
    /// ```
2622
    /// # use bevy_hanabi::*;
2623
    /// # use bevy::math::Vec3;
2624
    /// # let mut w = ExprWriter::new();
2625
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2626
    /// let x = w.lit(Vec3::ONE);
2627
    ///
2628
    /// // Asin: `y = asin(x);`
2629
    /// let y = x.asin();
2630
    /// ```
2631
    #[inline]
2632
    pub fn asin(self) -> Self {
×
2633
        self.unary_op(UnaryOperator::Asin)
×
2634
    }
2635

2636
    /// Apply the "atan" (inverse tangent) operator to the current float scalar
2637
    /// or vector expression.
2638
    ///
2639
    /// This is a unary operator, which applies to float scalar or vector
2640
    /// operand expressions to produce a float scalar or vector. It applies
2641
    /// component-wise to vector operand expressions. The return value lies in
2642
    /// the -π/2 ≤ x ≤ π/2 range.
2643
    ///
2644
    /// # Example
2645
    ///
2646
    /// ```
2647
    /// # use bevy_hanabi::*;
2648
    /// # use bevy::math::Vec3;
2649
    /// # let mut w = ExprWriter::new();
2650
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2651
    /// let x = w.lit(Vec3::ONE);
2652
    ///
2653
    /// // Atan: `y = atan(x);`
2654
    /// let y = x.atan();
2655
    /// ```
2656
    #[inline]
2657
    pub fn atan(self) -> Self {
×
2658
        self.unary_op(UnaryOperator::Atan)
×
2659
    }
2660

2661
    /// Apply the "ceil" operator to the current float scalar or vector
2662
    /// expression.
2663
    ///
2664
    /// This is a unary operator, which applies to float scalar or vector
2665
    /// operand expressions to produce a float scalar or vector. It applies
2666
    /// component-wise to vector operand expressions.
2667
    ///
2668
    /// # Example
2669
    ///
2670
    /// ```
2671
    /// # use bevy_hanabi::*;
2672
    /// # use bevy::math::Vec3;
2673
    /// # let mut w = ExprWriter::new();
2674
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2675
    /// let x = w.lit(Vec3::ONE);
2676
    ///
2677
    /// // Ceil: `y = ceil(x);`
2678
    /// let y = x.ceil();
2679
    /// ```
2680
    #[inline]
2681
    pub fn ceil(self) -> Self {
×
2682
        self.unary_op(UnaryOperator::Ceil)
×
2683
    }
2684

2685
    /// Apply the "cos" operator to the current float scalar or vector
2686
    /// expression.
2687
    ///
2688
    /// This is a unary operator, which applies to float scalar or vector
2689
    /// operand expressions to produce a float scalar or vector. It applies
2690
    /// component-wise to vector operand expressions.
2691
    ///
2692
    /// # Example
2693
    ///
2694
    /// ```
2695
    /// # use bevy_hanabi::*;
2696
    /// # use bevy::math::Vec3;
2697
    /// # let mut w = ExprWriter::new();
2698
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2699
    /// let x = w.lit(Vec3::ONE);
2700
    ///
2701
    /// // Cos: `y = cos(x);`
2702
    /// let y = x.cos();
2703
    /// ```
2704
    #[inline]
2705
    pub fn cos(self) -> Self {
×
2706
        self.unary_op(UnaryOperator::Cos)
×
2707
    }
2708

2709
    /// Apply the "exp" operator to the current float scalar or vector
2710
    /// expression.
2711
    ///
2712
    /// This is a unary operator, which applies to float scalar or vector
2713
    /// operand expressions to produce a float scalar or vector. It applies
2714
    /// component-wise to vector operand expressions.
2715
    ///
2716
    /// # Example
2717
    ///
2718
    /// ```
2719
    /// # use bevy_hanabi::*;
2720
    /// # use bevy::math::Vec3;
2721
    /// # let mut w = ExprWriter::new();
2722
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2723
    /// let x = w.lit(Vec3::ONE);
2724
    ///
2725
    /// // Exp: `y = exp(x);`
2726
    /// let y = x.exp();
2727
    /// ```
2728
    #[inline]
2729
    pub fn exp(self) -> Self {
×
2730
        self.unary_op(UnaryOperator::Exp)
×
2731
    }
2732

2733
    /// Apply the "exp2" operator to the current float scalar or vector
2734
    /// expression.
2735
    ///
2736
    /// This is a unary operator, which applies to float scalar or vector
2737
    /// operand expressions to produce a float scalar or vector. It applies
2738
    /// component-wise to vector operand expressions.
2739
    ///
2740
    /// # Example
2741
    ///
2742
    /// ```
2743
    /// # use bevy_hanabi::*;
2744
    /// # use bevy::math::Vec3;
2745
    /// # let mut w = ExprWriter::new();
2746
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2747
    /// let x = w.lit(Vec3::ONE);
2748
    ///
2749
    /// // Exp2: `y = exp2(x);`
2750
    /// let y = x.exp2();
2751
    /// ```
2752
    #[inline]
2753
    pub fn exp2(self) -> Self {
×
2754
        self.unary_op(UnaryOperator::Exp2)
×
2755
    }
2756

2757
    /// Apply the "floor" operator to the current float scalar or vector
2758
    /// expression.
2759
    ///
2760
    /// This is a unary operator, which applies to float scalar or vector
2761
    /// operand expressions to produce a float scalar or vector. It applies
2762
    /// component-wise to vector operand expressions.
2763
    ///
2764
    /// # Example
2765
    ///
2766
    /// ```
2767
    /// # use bevy_hanabi::*;
2768
    /// # use bevy::math::Vec3;
2769
    /// # let mut w = ExprWriter::new();
2770
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2771
    /// let x = w.lit(Vec3::ONE);
2772
    ///
2773
    /// // Floor: `y = floor(x);`
2774
    /// let y = x.floor();
2775
    /// ```
2776
    #[inline]
2777
    pub fn floor(self) -> Self {
×
2778
        self.unary_op(UnaryOperator::Floor)
×
2779
    }
2780

2781
    /// Apply the "fract" operator to the current float scalar or vector
2782
    /// expression.
2783
    ///
2784
    /// This is a unary operator, which applies to float scalar or vector
2785
    /// operand expressions to produce a float scalar or vector. It applies
2786
    /// component-wise to vector operand expressions.
2787
    ///
2788
    /// # Example
2789
    ///
2790
    /// ```
2791
    /// # use bevy_hanabi::*;
2792
    /// # use bevy::math::Vec3;
2793
    /// # let mut w = ExprWriter::new();
2794
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2795
    /// let x = w.lit(Vec3::ONE);
2796
    ///
2797
    /// // Fract: `y = fract(x);`
2798
    /// let y = x.fract();
2799
    /// ```
2800
    #[inline]
2801
    pub fn fract(self) -> Self {
×
2802
        self.unary_op(UnaryOperator::Fract)
×
2803
    }
2804

2805
    /// Apply the "inverseSqrt" (inverse square root) operator to the current
2806
    /// float scalar or vector expression.
2807
    ///
2808
    /// This is a unary operator, which applies to float scalar or vector
2809
    /// operand expressions to produce a float scalar or vector. It applies
2810
    /// component-wise to vector operand expressions.
2811
    ///
2812
    /// # Example
2813
    ///
2814
    /// ```
2815
    /// # use bevy_hanabi::*;
2816
    /// # use bevy::math::Vec3;
2817
    /// # let mut w = ExprWriter::new();
2818
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2819
    /// let x = w.lit(Vec3::ONE);
2820
    ///
2821
    /// // Inverse square root: `y = inverseSqrt(x) = 1.0 / sqrt(x);`
2822
    /// let y = x.inverse_sqrt();
2823
    /// ```
2824
    #[inline]
2825
    pub fn inverse_sqrt(self) -> Self {
×
2826
        self.unary_op(UnaryOperator::InvSqrt)
×
2827
    }
2828

2829
    /// Apply the "length" operator to the current float scalar or vector
2830
    /// expression.
2831
    ///
2832
    /// This is a unary operator, which applies to float scalar or vector
2833
    /// operand expressions to produce a float scalar or vector. It applies
2834
    /// component-wise to vector operand expressions.
2835
    ///
2836
    /// # Example
2837
    ///
2838
    /// ```
2839
    /// # use bevy_hanabi::*;
2840
    /// # use bevy::math::Vec3;
2841
    /// # let mut w = ExprWriter::new();
2842
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2843
    /// let x = w.lit(Vec3::ONE);
2844
    ///
2845
    /// // Length: `y = length(x);`
2846
    /// let y = x.length();
2847
    /// ```
2848
    #[inline]
2849
    pub fn length(self) -> Self {
×
2850
        self.unary_op(UnaryOperator::Length)
×
2851
    }
2852

2853
    /// Apply the "log" operator to the current float scalar or vector
2854
    /// expression.
2855
    ///
2856
    /// This is a unary operator, which applies to float scalar or vector
2857
    /// operand expressions to produce a float scalar or vector. It applies
2858
    /// component-wise to vector operand expressions.
2859
    ///
2860
    /// # Example
2861
    ///
2862
    /// ```
2863
    /// # use bevy_hanabi::*;
2864
    /// # use bevy::math::Vec3;
2865
    /// # let mut w = ExprWriter::new();
2866
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2867
    /// let x = w.lit(Vec3::ONE);
2868
    ///
2869
    /// // Log: `y = log(x);`
2870
    /// let y = x.log();
2871
    /// ```
2872
    #[inline]
2873
    pub fn log(self) -> Self {
×
2874
        self.unary_op(UnaryOperator::Log)
×
2875
    }
2876

2877
    /// Apply the "log2" operator to the current float scalar or vector
2878
    /// expression.
2879
    ///
2880
    /// This is a unary operator, which applies to float scalar or vector
2881
    /// operand expressions to produce a float scalar or vector. It applies
2882
    /// component-wise to vector operand expressions.
2883
    ///
2884
    /// # Example
2885
    ///
2886
    /// ```
2887
    /// # use bevy_hanabi::*;
2888
    /// # use bevy::math::Vec3;
2889
    /// # let mut w = ExprWriter::new();
2890
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2891
    /// let x = w.lit(Vec3::ONE);
2892
    ///
2893
    /// // Log2: `y = log2(x);`
2894
    /// let y = x.log2();
2895
    /// ```
2896
    #[inline]
2897
    pub fn log2(self) -> Self {
×
2898
        self.unary_op(UnaryOperator::Log2)
×
2899
    }
2900

2901
    /// Apply the "normalize" operator to the current float vector expression.
2902
    ///
2903
    /// This is a unary operator, which applies to float vector operand
2904
    /// expressions to produce another float vector with unit length
2905
    /// (normalized).
2906
    ///
2907
    /// # Example
2908
    ///
2909
    /// ```
2910
    /// # use bevy_hanabi::*;
2911
    /// # use bevy::math::Vec3;
2912
    /// # let mut w = ExprWriter::new();
2913
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2914
    /// let x = w.lit(Vec3::ONE);
2915
    ///
2916
    /// // Normalize: `y = normalize(x);`
2917
    /// let y = x.normalized();
2918
    /// ```
2919
    #[inline]
2920
    pub fn normalized(self) -> Self {
×
2921
        self.unary_op(UnaryOperator::Normalize)
×
2922
    }
2923

2924
    /// Apply the "pack4x8snorm" operator to the current 4-component float
2925
    /// vector expression.
2926
    ///
2927
    /// This is a unary operator, which applies to 4-component float vector
2928
    /// operand expressions to produce a single `u32` scalar expression.
2929
    ///
2930
    /// # Example
2931
    ///
2932
    /// ```
2933
    /// # use bevy_hanabi::*;
2934
    /// # use bevy::math::Vec4;
2935
    /// # let mut w = ExprWriter::new();
2936
    /// // A literal expression `x = vec4<f32>(-1., 1., 0., 7.2);`.
2937
    /// let x = w.lit(Vec4::new(-1., 1., 0., 7.2));
2938
    ///
2939
    /// // Pack: `y = pack4x8snorm(x);`
2940
    /// let y = x.pack4x8snorm(); // 0x7F007FFFu32
2941
    /// ```
2942
    #[inline]
2943
    pub fn pack4x8snorm(self) -> Self {
×
2944
        self.unary_op(UnaryOperator::Pack4x8snorm)
×
2945
    }
2946

2947
    /// Apply the "pack4x8unorm" operator to the current 4-component float
2948
    /// vector expression.
2949
    ///
2950
    /// This is a unary operator, which applies to 4-component float vector
2951
    /// operand expressions to produce a single `u32` scalar expression.
2952
    ///
2953
    /// # Example
2954
    ///
2955
    /// ```
2956
    /// # use bevy_hanabi::*;
2957
    /// # use bevy::math::Vec4;
2958
    /// # let mut w = ExprWriter::new();
2959
    /// // A literal expression `x = vec4<f32>(-1., 1., 0., 7.2);`.
2960
    /// let x = w.lit(Vec4::new(-1., 1., 0., 7.2));
2961
    ///
2962
    /// // Pack: `y = pack4x8unorm(x);`
2963
    /// let y = x.pack4x8unorm(); // 0xFF00FF00u32
2964
    /// ```
2965
    #[inline]
2966
    pub fn pack4x8unorm(self) -> Self {
×
2967
        self.unary_op(UnaryOperator::Pack4x8unorm)
×
2968
    }
2969

2970
    /// Apply the "round" operator to the current float scalar or vector
2971
    /// expression.
2972
    ///
2973
    /// This is a unary operator, which applies to float scalar or vector
2974
    /// operand expressions to produce a float scalar or vector. It applies
2975
    /// component-wise to vector operand expressions.
2976
    ///
2977
    /// # Example
2978
    ///
2979
    /// ```
2980
    /// # use bevy_hanabi::*;
2981
    /// # use bevy::math::Vec3;
2982
    /// # let mut w = ExprWriter::new();
2983
    /// // A literal expression `x = vec3<f32>(1.5, -3.2, 0.75);`.
2984
    /// let x = w.lit(Vec3::new(1.5, -3.2, 0.75));
2985
    ///
2986
    /// // Sign: `y = round(x);`
2987
    /// let y = x.round();
2988
    /// ```
2989
    #[inline]
2990
    pub fn round(self) -> Self {
×
2991
        self.unary_op(UnaryOperator::Round)
×
2992
    }
2993

2994
    /// Apply the "sign" operator to the current float scalar or vector
2995
    /// expression.
2996
    ///
2997
    /// This is a unary operator, which applies to float scalar or vector
2998
    /// operand expressions to produce a float scalar or vector. It applies
2999
    /// component-wise to vector operand expressions.
3000
    ///
3001
    /// # Example
3002
    ///
3003
    /// ```
3004
    /// # use bevy_hanabi::*;
3005
    /// # use bevy::math::Vec3;
3006
    /// # let mut w = ExprWriter::new();
3007
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3008
    /// let x = w.lit(Vec3::ONE);
3009
    ///
3010
    /// // Sign: `y = sign(x);`
3011
    /// let y = x.sign();
3012
    /// ```
3013
    #[inline]
3014
    pub fn sign(self) -> Self {
×
3015
        self.unary_op(UnaryOperator::Sign)
×
3016
    }
3017

3018
    /// Apply the "sin" operator to the current float scalar or vector
3019
    /// expression.
3020
    ///
3021
    /// This is a unary operator, which applies to float scalar or vector
3022
    /// operand expressions to produce a float scalar or vector. It applies
3023
    /// component-wise to vector operand expressions.
3024
    ///
3025
    /// # Example
3026
    ///
3027
    /// ```
3028
    /// # use bevy_hanabi::*;
3029
    /// # use bevy::math::Vec3;
3030
    /// # let mut w = ExprWriter::new();
3031
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3032
    /// let x = w.lit(Vec3::ONE);
3033
    ///
3034
    /// // Sin: `y = sin(x);`
3035
    /// let y = x.sin();
3036
    /// ```
3037
    #[inline]
3038
    pub fn sin(self) -> Self {
×
3039
        self.unary_op(UnaryOperator::Sin)
×
3040
    }
3041

3042
    /// Apply the "sqrt" (square root) operator to the current float scalar or
3043
    /// vector expression.
3044
    ///
3045
    /// This is a unary operator, which applies to float scalar or vector
3046
    /// operand expressions to produce a float scalar or vector. It applies
3047
    /// component-wise to vector operand expressions.
3048
    ///
3049
    /// # Example
3050
    ///
3051
    /// ```
3052
    /// # use bevy_hanabi::*;
3053
    /// # use bevy::math::Vec3;
3054
    /// # let mut w = ExprWriter::new();
3055
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3056
    /// let x = w.lit(Vec3::ONE);
3057
    ///
3058
    /// // Square root: `y = sqrt(x);`
3059
    /// let y = x.sqrt();
3060
    /// ```
3061
    #[inline]
3062
    pub fn sqrt(self) -> Self {
×
3063
        self.unary_op(UnaryOperator::Sqrt)
×
3064
    }
3065

3066
    /// Apply the "tan" operator to the current float scalar or vector
3067
    /// expression.
3068
    ///
3069
    /// This is a unary operator, which applies to float scalar or vector
3070
    /// operand expressions to produce a float scalar or vector. It applies
3071
    /// component-wise to vector operand expressions.
3072
    ///
3073
    /// # Example
3074
    ///
3075
    /// ```
3076
    /// # use bevy_hanabi::*;
3077
    /// # use bevy::math::Vec3;
3078
    /// # let mut w = ExprWriter::new();
3079
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3080
    /// let x = w.lit(Vec3::ONE);
3081
    ///
3082
    /// // Tan: `y = tan(x);`
3083
    /// let y = x.tan();
3084
    /// ```
3085
    #[inline]
3086
    pub fn tan(self) -> Self {
×
3087
        self.unary_op(UnaryOperator::Tan)
×
3088
    }
3089

3090
    /// Apply the "unpack4x8snorm" operator to the current `u32` scalar
3091
    /// expression.
3092
    ///
3093
    /// This is a unary operator, which applies to `u32` scalar operand
3094
    /// expressions to produce a 4-component floating point vector of signed
3095
    /// normalized components in `[-1:1]`.
3096
    ///
3097
    /// # Example
3098
    ///
3099
    /// ```
3100
    /// # use bevy_hanabi::*;
3101
    /// # use bevy::math::Vec3;
3102
    /// # let mut w = ExprWriter::new();
3103
    /// // A literal expression `y = 0x7F007FFFu32;`.
3104
    /// let y = w.lit(0x7F007FFFu32);
3105
    ///
3106
    /// // Unpack: `x = unpack4x8snorm(y);`
3107
    /// let x = y.unpack4x8snorm(); // vec4<f32>(-1., 1., 0., 7.2)
3108
    /// ```
3109
    #[inline]
3110
    pub fn unpack4x8snorm(self) -> Self {
×
3111
        self.unary_op(UnaryOperator::Unpack4x8snorm)
×
3112
    }
3113

3114
    /// Apply the "unpack4x8unorm" operator to the current `u32` scalar
3115
    /// expression.
3116
    ///
3117
    /// This is a unary operator, which applies to `u32` scalar operand
3118
    /// expressions to produce a 4-component floating point vector of unsigned
3119
    /// normalized components in `[0:1]`.
3120
    ///
3121
    /// # Example
3122
    ///
3123
    /// ```
3124
    /// # use bevy_hanabi::*;
3125
    /// # use bevy::math::Vec3;
3126
    /// # let mut w = ExprWriter::new();
3127
    /// // A literal expression `y = 0xFF00FF00u32;`.
3128
    /// let y = w.lit(0xFF00FF00u32);
3129
    ///
3130
    /// // Unpack: `x = unpack4x8unorm(y);`
3131
    /// let x = y.unpack4x8unorm(); // vec4<f32>(-1., 1., 0., 7.2)
3132
    /// ```
3133
    #[inline]
3134
    pub fn unpack4x8unorm(self) -> Self {
×
3135
        self.unary_op(UnaryOperator::Unpack4x8unorm)
×
3136
    }
3137

3138
    /// Apply the "saturate" operator to the current float scalar or vector
3139
    /// expression.
3140
    ///
3141
    /// This is a unary operator, which applies to float scalar or vector
3142
    /// operand expressions to produce a float scalar or vector. It applies
3143
    /// component-wise to vector operand expressions.
3144
    ///
3145
    /// # Example
3146
    ///
3147
    /// ```
3148
    /// # use bevy_hanabi::*;
3149
    /// # use bevy::math::Vec3;
3150
    /// # let mut w = ExprWriter::new();
3151
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3152
    /// let x = w.lit(Vec3::ONE);
3153
    ///
3154
    /// // Saturate: `y = saturate(x);`
3155
    /// let y = x.saturate();
3156
    /// ```
3157
    #[inline]
3158
    pub fn saturate(self) -> Self {
×
3159
        self.unary_op(UnaryOperator::Saturate)
×
3160
    }
3161

3162
    /// Get the first component of a scalar or vector.
3163
    ///
3164
    /// # Example
3165
    ///
3166
    /// ```
3167
    /// # use bevy_hanabi::*;
3168
    /// # use bevy::math::Vec3;
3169
    /// # let mut w = ExprWriter::new();
3170
    /// // A literal expression `v = vec3<f32>(1., 1., 1.);`.
3171
    /// let v = w.lit(Vec3::ONE);
3172
    ///
3173
    /// // f = v.x;`
3174
    /// let f = v.x();
3175
    /// ```
3176
    #[inline]
3177
    pub fn x(self) -> Self {
×
3178
        self.unary_op(UnaryOperator::X)
×
3179
    }
3180

3181
    /// Get the second component of a vector.
3182
    ///
3183
    /// # Example
3184
    ///
3185
    /// ```
3186
    /// # use bevy_hanabi::*;
3187
    /// # use bevy::math::Vec3;
3188
    /// # let mut w = ExprWriter::new();
3189
    /// // A literal expression `v = vec3<f32>(1., 1., 1.);`.
3190
    /// let v = w.lit(Vec3::ONE);
3191
    ///
3192
    /// // f = v.y;`
3193
    /// let f = v.y();
3194
    /// ```
3195
    #[inline]
3196
    pub fn y(self) -> Self {
×
3197
        self.unary_op(UnaryOperator::Y)
×
3198
    }
3199

3200
    /// Get the third component of a vector.
3201
    ///
3202
    /// # Example
3203
    ///
3204
    /// ```
3205
    /// # use bevy_hanabi::*;
3206
    /// # use bevy::math::Vec3;
3207
    /// # let mut w = ExprWriter::new();
3208
    /// // A literal expression `v = vec3<f32>(1., 1., 1.);`.
3209
    /// let v = w.lit(Vec3::ONE);
3210
    ///
3211
    /// // f = v.z;`
3212
    /// let f = v.z();
3213
    /// ```
3214
    #[inline]
3215
    pub fn z(self) -> Self {
×
3216
        self.unary_op(UnaryOperator::Z)
×
3217
    }
3218

3219
    /// Get the fourth component of a vector.
3220
    ///
3221
    /// # Example
3222
    ///
3223
    /// ```
3224
    /// # use bevy_hanabi::*;
3225
    /// # use bevy::math::Vec3;
3226
    /// # let mut w = ExprWriter::new();
3227
    /// // A literal expression `v = vec3<f32>(1., 1., 1.);`.
3228
    /// let v = w.lit(Vec3::ONE);
3229
    ///
3230
    /// // f = v.w;`
3231
    /// let f = v.w();
3232
    /// ```
3233
    #[inline]
3234
    pub fn w(self) -> Self {
×
3235
        self.unary_op(UnaryOperator::W)
×
3236
    }
3237

3238
    fn binary_op(self, other: Self, op: BinaryOperator) -> Self {
5✔
3239
        assert_eq!(self.module, other.module);
5✔
3240
        let left = self.expr;
10✔
3241
        let right = other.expr;
10✔
3242
        let expr = self
15✔
3243
            .module
10✔
3244
            .borrow_mut()
3245
            .push(Expr::Binary { op, left, right });
15✔
3246
        WriterExpr {
3247
            expr,
3248
            module: self.module,
5✔
3249
        }
3250
    }
3251

3252
    /// Add the current expression with another expression.
3253
    ///
3254
    /// This is a binary operator, which applies component-wise to vector
3255
    /// operand expressions.
3256
    ///
3257
    /// You can also use the [`std::ops::Add`] trait directly, via the `+`
3258
    /// symbol, as an alternative to calling this method directly.
3259
    ///
3260
    /// # Example
3261
    ///
3262
    /// ```
3263
    /// # use bevy_hanabi::*;
3264
    /// # use bevy::math::Vec2;
3265
    /// # let mut w = ExprWriter::new();
3266
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3267
    /// let x = w.lit(Vec2::new(3., -2.));
3268
    ///
3269
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3270
    /// let y = w.lit(Vec2::new(1., 5.));
3271
    ///
3272
    /// // The sum of both vectors `z = x + y;`.
3273
    /// let z = x.add(y); // == vec2<f32>(4., 3.)
3274
    ///                   // -OR-
3275
    ///                   // let z = x + y;
3276
    /// ```
3277
    #[allow(clippy::should_implement_trait)]
3278
    #[inline]
3279
    pub fn add(self, other: Self) -> Self {
2✔
3280
        self.binary_op(other, BinaryOperator::Add)
8✔
3281
    }
3282

3283
    /// Apply the "atan2" (inverse tangent with 2 arguments) operator to the
3284
    /// current float scalar or vector expression.
3285
    ///
3286
    /// This is a unary operator, which applies to float scalar or vector
3287
    /// operand expressions to produce a float scalar or vector. It applies
3288
    /// component-wise to vector operand expressions. The return value lies in
3289
    /// the -π ≤ x ≤ π range, and represents a value whose tangent is equal to
3290
    /// y over x (`z = atan2(y, x)` <=> `tan(z) = y / x`).
3291
    ///
3292
    /// # Example
3293
    ///
3294
    /// ```
3295
    /// # use bevy_hanabi::*;
3296
    /// # use bevy::math::Vec3;
3297
    /// # let mut w = ExprWriter::new();
3298
    /// // Two literal expressions `x` and `y`.
3299
    /// let x = w.lit(Vec3::new(1., 0., -1.));
3300
    /// let y = w.lit(Vec3::ONE);
3301
    ///
3302
    /// // Atan: `z = atan2(y, x);`
3303
    /// let z = y.atan2(x);
3304
    /// ```
3305
    #[inline]
3306
    pub fn atan2(self, other: Self) -> Self {
×
3307
        self.binary_op(other, BinaryOperator::Atan2)
×
3308
    }
3309

3310
    /// Calculate the cross product of the current expression by another
3311
    /// expression.
3312
    ///
3313
    /// This is a binary operator, which applies to vector operands of size 3
3314
    /// only, and always produces a vector of the same size.
3315
    ///
3316
    /// # Example
3317
    ///
3318
    /// ```
3319
    /// # use bevy_hanabi::*;
3320
    /// # use bevy::math::Vec3;
3321
    /// # let mut w = ExprWriter::new();
3322
    /// // A literal expression `x = vec3<f32>(3., -2., 1.);`.
3323
    /// let x = w.lit(Vec3::new(3., -2., 1.));
3324
    ///
3325
    /// // Another literal expression `y = vec3<f32>(1., 5., 0.);`.
3326
    /// let y = w.lit(Vec3::new(1., 5., 0.));
3327
    ///
3328
    /// // The cross product of both vectors `z = cross(x, y);`.
3329
    /// let z = x.cross(y);
3330
    /// ```
3331
    #[inline]
3332
    pub fn cross(self, other: Self) -> Self {
×
3333
        self.binary_op(other, BinaryOperator::Cross)
×
3334
    }
3335

3336
    /// Calculate the dot product of the current expression by another
3337
    /// expression.
3338
    ///
3339
    /// This is a binary operator, which applies to vector operands of same size
3340
    /// only, and always produces a floating point scalar.
3341
    ///
3342
    /// # Example
3343
    ///
3344
    /// ```
3345
    /// # use bevy_hanabi::*;
3346
    /// # use bevy::math::Vec2;
3347
    /// # let mut w = ExprWriter::new();
3348
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3349
    /// let x = w.lit(Vec2::new(3., -2.));
3350
    ///
3351
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3352
    /// let y = w.lit(Vec2::new(1., 5.));
3353
    ///
3354
    /// // The dot product of both vectors `z = dot(x, y);`.
3355
    /// let z = x.dot(y);
3356
    /// ```
3357
    #[inline]
3358
    pub fn dot(self, other: Self) -> Self {
×
3359
        self.binary_op(other, BinaryOperator::Dot)
×
3360
    }
3361

3362
    /// Calculate the distance between the current expression and another
3363
    /// expression.
3364
    ///
3365
    /// This is a binary operator.
3366
    ///
3367
    /// # Example
3368
    ///
3369
    /// ```
3370
    /// # use bevy_hanabi::*;
3371
    /// # use bevy::math::Vec3;
3372
    /// # let mut w = ExprWriter::new();
3373
    /// // A literal expression `x = vec3<f32>(3., -2., 1.);`.
3374
    /// let x = w.lit(Vec3::new(3., -2., 1.));
3375
    ///
3376
    /// // Another literal expression `y = vec3<f32>(1., 5., 0.);`.
3377
    /// let y = w.lit(Vec3::new(1., 5., 0.));
3378
    ///
3379
    /// // The distance between the vectors `z = distance(x, y);`.
3380
    /// let z = x.distance(y);
3381
    /// ```
3382
    #[inline]
3383
    pub fn distance(self, other: Self) -> Self {
×
3384
        self.binary_op(other, BinaryOperator::Distance)
×
3385
    }
3386

3387
    /// Divide the current expression by another expression.
3388
    ///
3389
    /// This is a binary operator, which applies component-wise to vector
3390
    /// operand expressions.
3391
    ///
3392
    /// You can also use the [`std::ops::Div`] trait directly, via the `/`
3393
    /// symbol, as an alternative to calling this method directly.
3394
    ///
3395
    /// # Example
3396
    ///
3397
    /// ```
3398
    /// # use bevy_hanabi::*;
3399
    /// # use bevy::math::Vec2;
3400
    /// # let mut w = ExprWriter::new();
3401
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3402
    /// let x = w.lit(Vec2::new(3., -2.));
3403
    ///
3404
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3405
    /// let y = w.lit(Vec2::new(1., 5.));
3406
    ///
3407
    /// // The quotient of both vectors `z = x / y;`.
3408
    /// let z = x.div(y); // == vec2<f32>(3., -0.4)
3409
    ///                   // -OR-
3410
    ///                   // let z = x / y;
3411
    /// ```
3412
    #[allow(clippy::should_implement_trait)]
3413
    #[inline]
3414
    pub fn div(self, other: Self) -> Self {
×
3415
        self.binary_op(other, BinaryOperator::Div)
×
3416
    }
3417

3418
    /// Apply the logical operator "greater than or equal" to this expression
3419
    /// and another expression.
3420
    ///
3421
    /// This is a binary operator, which applies component-wise to vector
3422
    /// operand expressions.
3423
    ///
3424
    /// # Example
3425
    ///
3426
    /// ```
3427
    /// # use bevy_hanabi::*;
3428
    /// # use bevy::math::Vec3;
3429
    /// # let mut w = ExprWriter::new();
3430
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3431
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3432
    ///
3433
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3434
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3435
    ///
3436
    /// // The boolean result of the greater than or equal operation `z = (x >= y);`.
3437
    /// let z = x.ge(y); // == vec3<bool>(true, false, true)
3438
    /// ```
3439
    #[inline]
3440
    pub fn ge(self, other: Self) -> Self {
×
3441
        self.binary_op(other, BinaryOperator::GreaterThanOrEqual)
×
3442
    }
3443

3444
    /// Apply the logical operator "greater than" to this expression and another
3445
    /// expression.
3446
    ///
3447
    /// This is a binary operator, which applies component-wise to vector
3448
    /// operand expressions.
3449
    ///
3450
    /// # Example
3451
    ///
3452
    /// ```
3453
    /// # use bevy_hanabi::*;
3454
    /// # use bevy::math::Vec3;
3455
    /// # let mut w = ExprWriter::new();
3456
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3457
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3458
    ///
3459
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3460
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3461
    ///
3462
    /// // The boolean result of the greater than operation `z = (x > y);`.
3463
    /// let z = x.gt(y); // == vec3<bool>(true, false, false)
3464
    /// ```
3465
    #[inline]
3466
    pub fn gt(self, other: Self) -> Self {
×
3467
        self.binary_op(other, BinaryOperator::GreaterThan)
×
3468
    }
3469

3470
    /// Apply the logical operator "less than or equal" to this expression and
3471
    /// another expression.
3472
    ///
3473
    /// This is a binary operator, which applies component-wise to vector
3474
    /// operand expressions.
3475
    ///
3476
    /// # Example
3477
    ///
3478
    /// ```
3479
    /// # use bevy_hanabi::*;
3480
    /// # use bevy::math::Vec3;
3481
    /// # let mut w = ExprWriter::new();
3482
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3483
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3484
    ///
3485
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3486
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3487
    ///
3488
    /// // The boolean result of the less than or equal operation `z = (x <= y);`.
3489
    /// let z = x.le(y); // == vec3<bool>(false, true, true)
3490
    /// ```
3491
    #[inline]
3492
    pub fn le(self, other: Self) -> Self {
×
3493
        self.binary_op(other, BinaryOperator::LessThanOrEqual)
×
3494
    }
3495

3496
    /// Apply the logical operator "less than" to this expression and another
3497
    /// expression.
3498
    ///
3499
    /// This is a binary operator, which applies component-wise to vector
3500
    /// operand expressions.
3501
    ///
3502
    /// # Example
3503
    ///
3504
    /// ```
3505
    /// # use bevy_hanabi::*;
3506
    /// # use bevy::math::Vec3;
3507
    /// # let mut w = ExprWriter::new();
3508
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3509
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3510
    ///
3511
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3512
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3513
    ///
3514
    /// // The boolean result of the less than operation `z = (x < y);`.
3515
    /// let z = x.lt(y); // == vec3<bool>(false, true, false)
3516
    /// ```
3517
    #[inline]
3518
    pub fn lt(self, other: Self) -> Self {
×
3519
        self.binary_op(other, BinaryOperator::LessThan)
×
3520
    }
3521

3522
    /// Take the maximum value of the current expression and another expression.
3523
    ///
3524
    /// This is a binary operator, which applies component-wise to vector
3525
    /// operand expressions.
3526
    ///
3527
    /// # Example
3528
    ///
3529
    /// ```
3530
    /// # use bevy_hanabi::*;
3531
    /// # use bevy::math::Vec2;
3532
    /// # let mut w = ExprWriter::new();
3533
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3534
    /// let x = w.lit(Vec2::new(3., -2.));
3535
    ///
3536
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3537
    /// let y = w.lit(Vec2::new(1., 5.));
3538
    ///
3539
    /// // The maximum of both vectors `z = max(x, y);`.
3540
    /// let z = x.max(y); // == vec2<f32>(3., 5.)
3541
    /// ```
3542
    #[inline]
3543
    pub fn max(self, other: Self) -> Self {
1✔
3544
        self.binary_op(other, BinaryOperator::Max)
4✔
3545
    }
3546

3547
    /// Take the minimum value of the current expression and another expression.
3548
    ///
3549
    /// This is a binary operator, which applies component-wise to vector
3550
    /// operand expressions.
3551
    ///
3552
    /// # Example
3553
    ///
3554
    /// ```
3555
    /// # use bevy_hanabi::*;
3556
    /// # use bevy::math::Vec2;
3557
    /// # let mut w = ExprWriter::new();
3558
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3559
    /// let x = w.lit(Vec2::new(3., -2.));
3560
    ///
3561
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3562
    /// let y = w.lit(Vec2::new(1., 5.));
3563
    ///
3564
    /// // The minimum of both vectors `z = min(x, y);`.
3565
    /// let z = x.min(y); // == vec2<f32>(1., -2.)
3566
    /// ```
3567
    #[inline]
3568
    pub fn min(self, other: Self) -> Self {
1✔
3569
        self.binary_op(other, BinaryOperator::Min)
4✔
3570
    }
3571

3572
    /// Multiply the current expression with another expression.
3573
    ///
3574
    /// This is a binary operator, which applies component-wise to vector
3575
    /// operand expressions.
3576
    ///
3577
    /// You can also use the [`std::ops::Mul`] trait directly, via the `*`
3578
    /// symbol, as an alternative to calling this method directly.
3579
    ///
3580
    /// # Example
3581
    ///
3582
    /// ```
3583
    /// # use bevy_hanabi::*;
3584
    /// # use bevy::math::Vec2;
3585
    /// # let mut w = ExprWriter::new();
3586
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3587
    /// let x = w.lit(Vec2::new(3., -2.));
3588
    ///
3589
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3590
    /// let y = w.lit(Vec2::new(1., 5.));
3591
    ///
3592
    /// // The product of both vectors `z = x * y;`.
3593
    /// let z = x.mul(y); // == vec2<f32>(3., -10.)
3594
    ///                   // -OR-
3595
    ///                   // let z = x * y;
3596
    /// ```
3597
    #[allow(clippy::should_implement_trait)]
3598
    #[inline]
3599
    pub fn mul(self, other: Self) -> Self {
1✔
3600
        self.binary_op(other, BinaryOperator::Mul)
4✔
3601
    }
3602

3603
    /// Calculate the remainder of the division of the current expression by
3604
    /// another expression.
3605
    ///
3606
    /// This is a binary operator.
3607
    ///
3608
    /// # Example
3609
    ///
3610
    /// ```
3611
    /// # use bevy_hanabi::*;
3612
    /// # use bevy::math::Vec3;
3613
    /// # let mut w = ExprWriter::new();
3614
    /// // A literal expression `x = vec3<f32>(3., -2., 1.);`.
3615
    /// let x = w.lit(Vec3::new(3., -2., 1.));
3616
    ///
3617
    /// // Another literal expression `y = vec3<f32>(1., 5., 0.);`.
3618
    /// let y = w.lit(Vec3::new(1., 5., 0.));
3619
    ///
3620
    /// // The remainder of the division `z = x % y;`.
3621
    /// let z = x.rem(y);
3622
    /// ```
3623
    #[allow(clippy::should_implement_trait)]
3624
    #[inline]
3625
    pub fn rem(self, other: Self) -> Self {
×
3626
        self.binary_op(other, BinaryOperator::Remainder)
×
3627
    }
3628

3629
    /// Calculate the step of a value with respect to a reference.
3630
    ///
3631
    /// This is a binary operator, which applies component-wise to vector
3632
    /// operand expressions.
3633
    ///
3634
    /// # Example
3635
    ///
3636
    /// ```
3637
    /// # use bevy_hanabi::*;
3638
    /// # use bevy::math::Vec3;
3639
    /// # let mut w = ExprWriter::new();
3640
    /// // A literal expression `x = vec3<f32>(3., -2.);`.
3641
    /// let x = w.lit(Vec3::new(3., -2., 8.));
3642
    ///
3643
    /// // An edge reference `e = vec3<f32>(1., 5.);`.
3644
    /// let e = w.lit(Vec3::new(1., 5., 8.));
3645
    ///
3646
    /// // The step value
3647
    /// let s = x.step(e); // == vec3<f32>(1., 0., 1.)
3648
    /// ```
3649
    #[allow(clippy::should_implement_trait)]
3650
    #[inline]
3651
    pub fn step(self, edge: Self) -> Self {
×
3652
        // Note: order is step(edge, x) but x.step(edge)
3653
        edge.binary_op(self, BinaryOperator::Step)
×
3654
    }
3655

3656
    /// Subtract another expression from the current expression.
3657
    ///
3658
    /// This is a binary operator, which applies component-wise to vector
3659
    /// operand expressions.
3660
    ///
3661
    /// You can also use the [`std::ops::Sub`] trait directly, via the `-`
3662
    /// symbol, as an alternative to calling this method directly.
3663
    ///
3664
    /// # Example
3665
    ///
3666
    /// ```
3667
    /// # use bevy_hanabi::*;
3668
    /// # use bevy::math::Vec2;
3669
    /// # let mut w = ExprWriter::new();
3670
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3671
    /// let x = w.lit(Vec2::new(3., -2.));
3672
    ///
3673
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3674
    /// let y = w.lit(Vec2::new(1., 5.));
3675
    ///
3676
    /// // The difference of both vectors `z = x - y;`.
3677
    /// let z = x.sub(y); // == vec2<f32>(2., -7.)
3678
    ///                   // -OR-
3679
    ///                   // let z = x - y;
3680
    /// ```
3681
    #[allow(clippy::should_implement_trait)]
3682
    #[inline]
3683
    pub fn sub(self, other: Self) -> Self {
×
3684
        self.binary_op(other, BinaryOperator::Sub)
×
3685
    }
3686

3687
    /// Apply the logical operator "uniform" to this expression and another
3688
    /// expression.
3689
    ///
3690
    /// This is a binary operator, which applies component-wise to vector
3691
    /// operand expressions. That is, for vectors, this produces a vector of
3692
    /// random values where each component is uniformly distributed within the
3693
    /// bounds of the related component of both operands.
3694
    ///
3695
    /// # Example
3696
    ///
3697
    /// ```
3698
    /// # use bevy_hanabi::*;
3699
    /// # use bevy::math::Vec3;
3700
    /// # let mut w = ExprWriter::new();
3701
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3702
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3703
    ///
3704
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3705
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3706
    ///
3707
    /// // A random variable uniformly distributed in [1:3]x[-2:5]x[7:7].
3708
    /// let z = x.uniform(y);
3709
    /// ```
3710
    #[inline]
3711
    pub fn uniform(self, other: Self) -> Self {
×
3712
        self.binary_op(other, BinaryOperator::UniformRand)
×
3713
    }
3714

3715
    /// Apply the logical operator "normal" to this expression and another
3716
    /// expression.
3717
    ///
3718
    /// This is a binary operator, which applies component-wise to vector
3719
    /// operand expressions. That is, for vectors, this produces a vector of
3720
    /// random values where each component is normally distributed with a mean
3721
    /// of the corresponding component of the first operand and a standard
3722
    /// deviation of the corresponding component of the second operand.
3723
    ///
3724
    /// # Example
3725
    ///
3726
    /// ```
3727
    /// # use bevy_hanabi::*;
3728
    /// # use bevy::math::Vec3;
3729
    /// # let mut w = ExprWriter::new();
3730
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3731
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3732
    ///
3733
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3734
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3735
    ///
3736
    /// // A random variable normally distributed in [1:3]x[-2:5]x[7:7].
3737
    /// let z = x.normal(y);
3738
    /// ```
3739
    #[inline]
3740
    pub fn normal(self, other: Self) -> Self {
×
3741
        self.binary_op(other, BinaryOperator::NormalRand)
×
3742
    }
3743

3744
    fn ternary_op(self, second: Self, third: Self, op: TernaryOperator) -> Self {
×
3745
        assert_eq!(self.module, second.module);
×
3746
        assert_eq!(self.module, third.module);
×
3747
        let first = self.expr;
×
3748
        let second = second.expr;
×
3749
        let third = third.expr;
×
3750
        let expr = self.module.borrow_mut().push(Expr::Ternary {
×
3751
            op,
×
3752
            first,
×
3753
            second,
×
3754
            third,
×
3755
        });
3756
        WriterExpr {
3757
            expr,
3758
            module: self.module,
×
3759
        }
3760
    }
3761

3762
    /// Blending linearly ("mix") two expressions with the fraction provided by
3763
    /// a third expression.
3764
    ///
3765
    /// This is a ternary operator, which applies component-wise to vector
3766
    /// operand expressions.
3767
    ///
3768
    /// # Example
3769
    ///
3770
    /// ```
3771
    /// # use bevy_hanabi::*;
3772
    /// # use bevy::math::Vec2;
3773
    /// # let mut w = ExprWriter::new();
3774
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3775
    /// let x = w.lit(Vec2::new(3., -2.));
3776
    ///
3777
    /// // Another literal expression `y = vec2<f32>(1., 4.);`.
3778
    /// let y = w.lit(Vec2::new(1., 4.));
3779
    ///
3780
    /// // A fraction `t = 0.5;`.
3781
    /// let t = w.lit(0.25);
3782
    ///
3783
    /// // The linear blend of x and y via t: z = (1 - t) * x + y * t
3784
    /// let z = x.mix(y, t); // == vec2<f32>(2.5, -0.5)
3785
    /// ```
3786
    #[inline]
3787
    pub fn mix(self, other: Self, fraction: Self) -> Self {
×
3788
        self.ternary_op(other, fraction, TernaryOperator::Mix)
×
3789
    }
3790

3791
    /// Calculate the smooth Hermite interpolation in \[0:1\] of the current
3792
    /// value taken between the given bounds.
3793
    ///
3794
    /// This is a ternary operator, which applies component-wise to vector
3795
    /// operand expressions.
3796
    ///
3797
    /// # Example
3798
    ///
3799
    /// ```
3800
    /// # use bevy_hanabi::*;
3801
    /// # use bevy::math::Vec2;
3802
    /// # let mut w = ExprWriter::new();
3803
    /// // A literal expression `low = vec2<f32>(3., -2.);`.
3804
    /// let low = w.lit(Vec2::new(3., -2.));
3805
    ///
3806
    /// // Another literal expression `high = vec2<f32>(1., 4.);`.
3807
    /// let high = w.lit(Vec2::new(1., 4.));
3808
    ///
3809
    /// // A point `x = vec2<f32>(2., 1.);` between `low` and `high`.
3810
    /// let x = w.lit(Vec2::new(2., 1.));
3811
    ///
3812
    /// // The smooth Hermite interpolation: `t = smoothstep(low, high, x)`
3813
    /// let t = x.smoothstep(low, high); // == 0.5
3814
    /// ```
3815
    #[inline]
3816
    pub fn smoothstep(self, low: Self, high: Self) -> Self {
×
3817
        // Note: order is smoothstep(low, high, x) but x.smoothstep(low, high)
3818
        low.ternary_op(high, self, TernaryOperator::SmoothStep)
×
3819
    }
3820

3821
    /// Construct a `Vec2` from two scalars.
3822
    ///
3823
    /// # Example
3824
    ///
3825
    /// ```
3826
    /// # use bevy_hanabi::*;
3827
    /// # let mut w = ExprWriter::new();
3828
    /// let theta = w.add_property("theta", 0.0.into());
3829
    /// // Convert the angular property `theta` to a 2D vector.
3830
    /// let (cos_theta, sin_theta) = (w.prop(theta).cos(), w.prop(theta).sin());
3831
    /// let circle_pos = cos_theta.vec2(sin_theta);
3832
    /// ```
3833
    #[inline]
3834
    pub fn vec2(self, y: Self) -> Self {
×
3835
        self.binary_op(y, BinaryOperator::Vec2)
×
3836
    }
3837

3838
    /// Construct a `Vec3` from two scalars.
3839
    ///
3840
    /// # Example
3841
    ///
3842
    /// ```
3843
    /// # use bevy_hanabi::*;
3844
    /// # let mut w = ExprWriter::new();
3845
    /// let theta = w.add_property("theta", 0.0.into());
3846
    /// // Convert the angular property `theta` to a 3D vector in a flat plane.
3847
    /// let (cos_theta, sin_theta) = (w.prop(theta).cos(), w.prop(theta).sin());
3848
    /// let circle_pos = cos_theta.vec3(w.lit(0.0), sin_theta);
3849
    /// ```
3850
    #[inline]
3851
    pub fn vec3(self, y: Self, z: Self) -> Self {
×
3852
        self.ternary_op(y, z, TernaryOperator::Vec3)
×
3853
    }
3854

3855
    /// Construct a `Vec4` from a vector XYZ and a scalar W.
3856
    ///
3857
    /// # Example
3858
    ///
3859
    /// ```
3860
    /// # use bevy_hanabi::*;
3861
    /// # let mut w = ExprWriter::new();
3862
    /// let rgb = w.rand(VectorType::VEC3F);
3863
    /// let a = w.lit(1.);
3864
    /// // Build vec4<f32>(R, G, B, A) and convert to 0xAABBGGRR
3865
    /// let col = rgb.vec4_xyz_w(a).pack4x8unorm();
3866
    /// ```
3867
    #[inline]
3868
    pub fn vec4_xyz_w(self, w: Self) -> Self {
×
3869
        self.binary_op(w, BinaryOperator::Vec4XyzW)
×
3870
    }
3871

3872
    /// Cast an expression to a different type.
3873
    ///
3874
    /// # Example
3875
    ///
3876
    /// ```
3877
    /// # use bevy_hanabi::*;
3878
    /// # use bevy::math::Vec2;
3879
    /// # let mut w = ExprWriter::new();
3880
    /// let x = w.lit(Vec2::new(3., -2.));
3881
    /// let y = x.cast(VectorType::VEC3I); // x = vec3<i32>(particle.position);
3882
    /// ```
3883
    pub fn cast(self, target: impl Into<ValueType>) -> Self {
×
3884
        let target = target.into();
×
3885
        let expr = self
×
3886
            .module
×
3887
            .borrow_mut()
3888
            .push(Expr::Cast(CastExpr::new(self.expr, target)));
×
3889
        WriterExpr {
3890
            expr,
3891
            module: self.module,
×
3892
        }
3893
    }
3894

3895
    /// Finalize an expression chain and return the accumulated expression.
3896
    ///
3897
    /// The returned handle indexes the [`Module`] owned by the [`ExprWriter`]
3898
    /// this intermediate expression was built from.
3899
    ///
3900
    /// # Example
3901
    ///
3902
    /// ```
3903
    /// # use bevy_hanabi::*;
3904
    /// # let mut w = ExprWriter::new();
3905
    /// // A literal expression `x = -3.5;`.
3906
    /// let x = w.lit(-3.5);
3907
    ///
3908
    /// // Retrieve the ExprHandle for that expression.
3909
    /// let handle = x.expr();
3910
    /// ```
3911
    #[inline]
3912
    pub fn expr(self) -> ExprHandle {
8✔
3913
        self.expr
8✔
3914
    }
3915
}
3916

3917
impl std::ops::Add<WriterExpr> for WriterExpr {
3918
    type Output = WriterExpr;
3919

3920
    #[inline]
3921
    fn add(self, rhs: WriterExpr) -> Self::Output {
2✔
3922
        self.add(rhs)
6✔
3923
    }
3924
}
3925

3926
impl std::ops::Sub<WriterExpr> for WriterExpr {
3927
    type Output = WriterExpr;
3928

3929
    #[inline]
3930
    fn sub(self, rhs: WriterExpr) -> Self::Output {
×
3931
        self.sub(rhs)
×
3932
    }
3933
}
3934

3935
impl std::ops::Mul<WriterExpr> for WriterExpr {
3936
    type Output = WriterExpr;
3937

3938
    #[inline]
3939
    fn mul(self, rhs: WriterExpr) -> Self::Output {
1✔
3940
        self.mul(rhs)
3✔
3941
    }
3942
}
3943

3944
impl std::ops::Div<WriterExpr> for WriterExpr {
3945
    type Output = WriterExpr;
3946

3947
    #[inline]
3948
    fn div(self, rhs: WriterExpr) -> Self::Output {
×
3949
        self.div(rhs)
×
3950
    }
3951
}
3952

3953
impl std::ops::Rem<WriterExpr> for WriterExpr {
3954
    type Output = WriterExpr;
3955

3956
    #[inline]
3957
    fn rem(self, rhs: WriterExpr) -> Self::Output {
×
3958
        self.rem(rhs)
×
3959
    }
3960
}
3961

3962
#[cfg(test)]
3963
mod tests {
3964
    use bevy::{platform::collections::HashSet, prelude::*};
3965

3966
    use super::*;
3967
    use crate::{MatrixType, ScalarValue, ShaderWriter, VectorType};
3968

3969
    #[test]
3970
    fn module() {
3971
        let mut m = Module::default();
3972

3973
        #[allow(unsafe_code)]
3974
        let unknown = unsafe { ExprHandle::new_unchecked(1) };
3975
        assert!(m.get(unknown).is_none());
3976
        assert!(m.get_mut(unknown).is_none());
3977
        assert!(matches!(
3978
            m.try_get(unknown),
3979
            Err(ExprError::InvalidExprHandleError(_))
3980
        ));
3981
        assert!(matches!(
3982
            m.try_get_mut(unknown),
3983
            Err(ExprError::InvalidExprHandleError(_))
3984
        ));
3985

3986
        let x = m.lit(5.);
3987
        let mut expected = Expr::Literal(LiteralExpr::new(5.));
3988
        assert_eq!(m.get(x), Some(&expected));
3989
        assert_eq!(m.get_mut(x), Some(&mut expected));
3990
        assert_eq!(m.try_get(x), Ok(&expected));
3991
        assert_eq!(m.try_get_mut(x), Ok(&mut expected));
3992
    }
3993

3994
    #[test]
3995
    fn local_var() {
3996
        let property_layout = PropertyLayout::default();
3997
        let particle_layout = ParticleLayout::default();
3998
        let mut ctx =
3999
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4000
        let mut h = HashSet::new();
4001
        for _ in 0..100 {
4002
            let v = ctx.make_local_var();
4003
            assert!(h.insert(v));
4004
        }
4005
    }
4006

4007
    #[test]
4008
    fn make_fn() {
4009
        let property_layout = PropertyLayout::default();
4010
        let particle_layout = ParticleLayout::default();
4011
        let mut ctx =
4012
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4013
        let mut module = Module::default();
4014

4015
        // Make a function
4016
        let func_name = "my_func";
4017
        let args = "arg0: i32, arg1: f32";
4018
        assert!(ctx
4019
            .make_fn(func_name, args, &mut module, &mut |m, ctx| {
4020
                m.lit(3.);
4021
                let v = ctx.make_local_var();
4022
                assert_eq!(v, "var0");
4023
                let code = String::new();
4024
                Ok(code)
4025
            })
4026
            .is_ok());
4027

4028
        // The local function context doesn't influence the outer caller context
4029
        let v = ctx.make_local_var();
4030
        assert_eq!(v, "var0");
4031

4032
        // However the module is common to the outer caller and the function
4033
        assert!(!module.expressions.is_empty());
4034
    }
4035

4036
    #[test]
4037
    fn property() {
4038
        let mut m = Module::default();
4039

4040
        let _my_prop = m.add_property("my_prop", Value::Scalar(345_u32.into()));
4041
        let _other_prop = m.add_property(
4042
            "other_prop",
4043
            Value::Vector(Vec3::new(3., -7.5, 42.42).into()),
4044
        );
4045

4046
        assert!(m.properties().iter().any(|p| p.name() == "my_prop"));
4047
        assert!(m.properties().iter().any(|p| p.name() == "other_prop"));
4048
        assert!(!m.properties().iter().any(|p| p.name() == "do_not_exist"));
4049
    }
4050

4051
    #[test]
4052
    fn writer() {
4053
        // Get a module and its writer
4054
        let w = ExprWriter::new();
4055
        let my_prop = w.add_property("my_prop", 3.0.into());
4056

4057
        // Build some expression
4058
        let x = w.lit(3.).abs().max(w.attr(Attribute::POSITION) * w.lit(2.))
4059
            + w.lit(-4.).min(w.prop(my_prop));
4060
        let x = x.expr();
4061

4062
        // Create an evaluation context
4063
        let property_layout =
4064
            PropertyLayout::new(&[Property::new("my_prop", ScalarValue::Float(3.))]);
4065
        let particle_layout = ParticleLayout::default();
4066
        let m = w.finish();
4067
        let mut context =
4068
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4069

4070
        // Evaluate the expression
4071
        let x = m.try_get(x).unwrap();
4072
        let s = x.eval(&m, &mut context).unwrap();
4073
        assert_eq!(
4074
            "(max(abs(3.), (particle.position) * (2.))) + (min(-4., properties.my_prop))"
4075
                .to_string(),
4076
            s
4077
        );
4078
    }
4079

4080
    #[test]
4081
    fn type_error() {
4082
        let l = Value::Scalar(3.5_f32.into());
4083
        let r: Result<Vec2, ExprError> = l.try_into();
4084
        assert!(r.is_err());
4085
        assert!(matches!(r, Err(ExprError::TypeError(_))));
4086
    }
4087

4088
    #[test]
4089
    fn math_expr() {
4090
        let mut m = Module::default();
4091

4092
        let x = m.attr(Attribute::POSITION);
4093
        let y = m.lit(Vec3::ONE);
4094

4095
        let add = m.add(x, y);
4096
        let sub = m.sub(x, y);
4097
        let mul = m.mul(x, y);
4098
        let div = m.div(x, y);
4099
        let rem = m.rem(x, y);
4100
        let lt = m.lt(x, y);
4101
        let le = m.le(x, y);
4102
        let gt = m.gt(x, y);
4103
        let ge = m.ge(x, y);
4104

4105
        let property_layout = PropertyLayout::default();
4106
        let particle_layout = ParticleLayout::default();
4107
        let mut ctx =
4108
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4109

4110
        for (expr, op) in [
4111
            (add, "+"),
4112
            (sub, "-"),
4113
            (mul, "*"),
4114
            (div, "/"),
4115
            (rem, "%"),
4116
            (lt, "<"),
4117
            (le, "<="),
4118
            (gt, ">"),
4119
            (ge, ">="),
4120
        ] {
4121
            let expr = ctx.eval(&m, expr);
4122
            assert!(expr.is_ok());
4123
            let expr = expr.unwrap();
4124
            assert_eq!(
4125
                expr,
4126
                format!(
4127
                    "(particle.{}) {} (vec3<f32>(1.,1.,1.))",
4128
                    Attribute::POSITION.name(),
4129
                    op,
4130
                )
4131
            );
4132
        }
4133
    }
4134

4135
    #[test]
4136
    fn builtin_expr() {
4137
        let mut m = Module::default();
4138

4139
        // Simulation parameters
4140
        for op in [
4141
            BuiltInOperator::Time,
4142
            BuiltInOperator::DeltaTime,
4143
            BuiltInOperator::VirtualTime,
4144
            BuiltInOperator::VirtualDeltaTime,
4145
            BuiltInOperator::RealTime,
4146
            BuiltInOperator::RealDeltaTime,
4147
        ] {
4148
            let value = m.builtin(op);
4149

4150
            let property_layout = PropertyLayout::default();
4151
            let particle_layout = ParticleLayout::default();
4152
            let mut ctx =
4153
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4154

4155
            let expr = ctx.eval(&m, value);
4156
            assert!(expr.is_ok());
4157
            let expr = expr.unwrap();
4158
            assert_eq!(expr, format!("sim_params.{}", op.name()));
4159
        }
4160

4161
        // is_alive
4162
        {
4163
            let value = m.builtin(BuiltInOperator::IsAlive);
4164

4165
            let property_layout = PropertyLayout::default();
4166
            let particle_layout = ParticleLayout::default();
4167
            let mut ctx =
4168
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4169

4170
            let expr = ctx.eval(&m, value);
4171
            assert!(expr.is_ok());
4172
            let expr = expr.unwrap();
4173
            assert_eq!(expr, "is_alive");
4174
        }
4175

4176
        // BuiltInOperator::Rand (which has side effect)
4177
        for (scalar_type, prefix) in [
4178
            (ScalarType::Bool, "b"),
4179
            (ScalarType::Float, "f"),
4180
            (ScalarType::Int, "i"),
4181
            (ScalarType::Uint, "u"),
4182
        ] {
4183
            let value = m.builtin(BuiltInOperator::Rand(scalar_type.into()));
4184

4185
            // Scalar form
4186
            {
4187
                let property_layout = PropertyLayout::default();
4188
                let particle_layout = ParticleLayout::default();
4189
                let mut ctx =
4190
                    ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4191

4192
                let expr = ctx.eval(&m, value);
4193
                assert!(expr.is_ok());
4194
                let expr = expr.unwrap();
4195
                assert_eq!(expr, "var0");
4196
                assert_eq!(ctx.main_code, format!("let var0 = {}rand();\n", prefix));
4197
            }
4198

4199
            // Vector form
4200
            for count in 2..=4 {
4201
                let vec = m.builtin(BuiltInOperator::Rand(
4202
                    VectorType::new(scalar_type, count).into(),
4203
                ));
4204

4205
                let property_layout = PropertyLayout::default();
4206
                let particle_layout = ParticleLayout::default();
4207
                let mut ctx =
4208
                    ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4209

4210
                let expr = ctx.eval(&m, vec);
4211
                assert!(expr.is_ok());
4212
                let expr = expr.unwrap();
4213
                assert_eq!(expr, "var0");
4214
                assert_eq!(
4215
                    ctx.main_code,
4216
                    format!("let var0 = {}rand{}();\n", prefix, count)
4217
                );
4218
            }
4219
        }
4220
    }
4221

4222
    #[test]
4223
    fn unary_expr() {
4224
        let mut m = Module::default();
4225

4226
        let x = m.attr(Attribute::POSITION);
4227
        let y = m.lit(Vec3::new(1., -3.1, 6.99));
4228
        let z = m.lit(BVec3::new(false, true, false));
4229
        let w = m.lit(Vec4::W);
4230
        let v = m.lit(Vec4::new(-1., 1., 0., 7.2));
4231
        let us = m.lit(0x0u32);
4232
        let uu = m.lit(0x0u32);
4233

4234
        let abs = m.abs(x);
4235
        let acos = m.acos(w);
4236
        let all = m.all(z);
4237
        let any = m.any(z);
4238
        let asin = m.asin(w);
4239
        let atan = m.atan(w);
4240
        let ceil = m.ceil(y);
4241
        let cos = m.cos(y);
4242
        let exp = m.exp(y);
4243
        let exp2 = m.exp2(y);
4244
        let floor = m.floor(y);
4245
        let fract = m.fract(y);
4246
        let inv_sqrt = m.inverse_sqrt(y);
4247
        let length = m.length(y);
4248
        let log = m.log(y);
4249
        let log2 = m.log2(y);
4250
        let norm = m.normalize(y);
4251
        let pack4x8snorm = m.pack4x8snorm(v);
4252
        let pack4x8unorm = m.pack4x8unorm(v);
4253
        let round = m.round(y);
4254
        let saturate = m.saturate(y);
4255
        let sign = m.sign(y);
4256
        let sin = m.sin(y);
4257
        let sqrt = m.sqrt(y);
4258
        let tan = m.tan(y);
4259
        let unpack4x8snorm = m.unpack4x8snorm(us);
4260
        let unpack4x8unorm = m.unpack4x8unorm(uu);
4261
        let comp_x = m.x(w);
4262
        let comp_y = m.y(w);
4263
        let comp_z = m.z(w);
4264
        let comp_w = m.w(w);
4265

4266
        let property_layout = PropertyLayout::default();
4267
        let particle_layout = ParticleLayout::default();
4268
        let mut ctx =
4269
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4270

4271
        for (expr, op, inner) in [
4272
            (
4273
                abs,
4274
                "abs",
4275
                &format!("particle.{}", Attribute::POSITION.name())[..],
4276
            ),
4277
            (acos, "acos", "vec4<f32>(0.,0.,0.,1.)"),
4278
            (all, "all", "vec3<bool>(false,true,false)"),
4279
            (any, "any", "vec3<bool>(false,true,false)"),
4280
            (asin, "asin", "vec4<f32>(0.,0.,0.,1.)"),
4281
            (atan, "atan", "vec4<f32>(0.,0.,0.,1.)"),
4282
            (ceil, "ceil", "vec3<f32>(1.,-3.1,6.99)"),
4283
            (cos, "cos", "vec3<f32>(1.,-3.1,6.99)"),
4284
            (exp, "exp", "vec3<f32>(1.,-3.1,6.99)"),
4285
            (exp2, "exp2", "vec3<f32>(1.,-3.1,6.99)"),
4286
            (floor, "floor", "vec3<f32>(1.,-3.1,6.99)"),
4287
            (fract, "fract", "vec3<f32>(1.,-3.1,6.99)"),
4288
            (inv_sqrt, "inverseSqrt", "vec3<f32>(1.,-3.1,6.99)"),
4289
            (length, "length", "vec3<f32>(1.,-3.1,6.99)"),
4290
            (log, "log", "vec3<f32>(1.,-3.1,6.99)"),
4291
            (log2, "log2", "vec3<f32>(1.,-3.1,6.99)"),
4292
            (norm, "normalize", "vec3<f32>(1.,-3.1,6.99)"),
4293
            (pack4x8snorm, "pack4x8snorm", "vec4<f32>(-1.,1.,0.,7.2)"),
4294
            (pack4x8unorm, "pack4x8unorm", "vec4<f32>(-1.,1.,0.,7.2)"),
4295
            (round, "round", "vec3<f32>(1.,-3.1,6.99)"),
4296
            (saturate, "saturate", "vec3<f32>(1.,-3.1,6.99)"),
4297
            (sign, "sign", "vec3<f32>(1.,-3.1,6.99)"),
4298
            (sin, "sin", "vec3<f32>(1.,-3.1,6.99)"),
4299
            (sqrt, "sqrt", "vec3<f32>(1.,-3.1,6.99)"),
4300
            (tan, "tan", "vec3<f32>(1.,-3.1,6.99)"),
4301
            (unpack4x8snorm, "unpack4x8snorm", "0u"),
4302
            (unpack4x8unorm, "unpack4x8unorm", "0u"),
4303
        ] {
4304
            let expr = ctx.eval(&m, expr);
4305
            assert!(expr.is_ok());
4306
            let expr = expr.unwrap();
4307
            assert_eq!(expr, format!("{}({})", op, inner));
4308
        }
4309

4310
        for (expr, op, inner) in [
4311
            (comp_x, "x", "vec4<f32>(0.,0.,0.,1.)"),
4312
            (comp_y, "y", "vec4<f32>(0.,0.,0.,1.)"),
4313
            (comp_z, "z", "vec4<f32>(0.,0.,0.,1.)"),
4314
            (comp_w, "w", "vec4<f32>(0.,0.,0.,1.)"),
4315
        ] {
4316
            let expr = ctx.eval(&m, expr);
4317
            assert!(expr.is_ok());
4318
            let expr = expr.unwrap();
4319
            assert_eq!(expr, format!("{}.{}", inner, op));
4320
        }
4321
    }
4322

4323
    #[test]
4324
    fn binary_expr() {
4325
        let mut m = Module::default();
4326

4327
        let x = m.attr(Attribute::POSITION);
4328
        let y = m.lit(Vec3::ONE);
4329

4330
        let atan2 = m.atan2(x, y);
4331
        let cross = m.cross(x, y);
4332
        let dist = m.distance(x, y);
4333
        let dot = m.dot(x, y);
4334
        let min = m.min(x, y);
4335
        let max = m.max(x, y);
4336
        let step = m.step(x, y);
4337

4338
        let property_layout = PropertyLayout::default();
4339
        let particle_layout = ParticleLayout::default();
4340
        let mut ctx =
4341
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4342

4343
        for (expr, op) in [
4344
            (atan2, "atan2"),
4345
            (cross, "cross"),
4346
            (dist, "distance"),
4347
            (dot, "dot"),
4348
            (min, "min"),
4349
            (max, "max"),
4350
            (step, "step"),
4351
        ] {
4352
            let expr = ctx.eval(&m, expr);
4353
            assert!(expr.is_ok());
4354
            let expr = expr.unwrap();
4355
            assert_eq!(
4356
                expr,
4357
                format!(
4358
                    "{}(particle.{}, vec3<f32>(1.,1.,1.))",
4359
                    op,
4360
                    Attribute::POSITION.name(),
4361
                )
4362
            );
4363
        }
4364
    }
4365

4366
    #[test]
4367
    fn ternary_expr() {
4368
        let mut m = Module::default();
4369

4370
        let x = m.attr(Attribute::POSITION);
4371
        let y = m.lit(Vec3::ONE);
4372
        let t = m.lit(0.3);
4373

4374
        let mix = m.mix(x, y, t);
4375
        let smoothstep = m.smoothstep(x, y, x);
4376

4377
        let property_layout = PropertyLayout::default();
4378
        let particle_layout = ParticleLayout::default();
4379
        let mut ctx =
4380
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4381

4382
        for (expr, op, third) in [(mix, "mix", t), (smoothstep, "smoothstep", x)] {
4383
            let expr = ctx.eval(&m, expr);
4384
            assert!(expr.is_ok());
4385
            let expr = expr.unwrap();
4386
            let third = ctx.eval(&m, third).unwrap();
4387
            assert_eq!(
4388
                expr,
4389
                format!(
4390
                    "{}(particle.{}, vec3<f32>(1.,1.,1.), {})",
4391
                    op,
4392
                    Attribute::POSITION.name(),
4393
                    third
4394
                )
4395
            );
4396
        }
4397
    }
4398

4399
    #[test]
4400
    fn cast_expr() {
4401
        let mut m = Module::default();
4402

4403
        let x = m.attr(Attribute::POSITION);
4404
        let y = m.lit(IVec2::ONE);
4405
        let z = m.lit(0.3);
4406
        let w = m.lit(false);
4407

4408
        let cx = m.cast(x, VectorType::VEC3I);
4409
        let cy = m.cast(y, VectorType::VEC2U);
4410
        let cz = m.cast(z, ScalarType::Int);
4411
        let cw = m.cast(w, ScalarType::Uint);
4412

4413
        let property_layout = PropertyLayout::default();
4414
        let particle_layout = ParticleLayout::default();
4415
        let mut ctx =
4416
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4417

4418
        for (expr, cast, target) in [
4419
            (x, cx, ValueType::Vector(VectorType::VEC3I)),
4420
            (y, cy, VectorType::VEC2U.into()),
4421
            (z, cz, ScalarType::Int.into()),
4422
            (w, cw, ScalarType::Uint.into()),
4423
        ] {
4424
            let expr = ctx.eval(&m, expr);
4425
            assert!(expr.is_ok());
4426
            let expr = expr.unwrap();
4427
            let cast = ctx.eval(&m, cast);
4428
            assert!(cast.is_ok());
4429
            let cast = cast.unwrap();
4430
            assert_eq!(cast, format!("{}({})", target.to_wgsl_string(), expr));
4431
        }
4432
    }
4433

4434
    #[test]
4435
    fn attribute_pointer() {
4436
        let mut m = Module::default();
4437
        let x = m.attr(Attribute::POSITION);
4438

4439
        let property_layout = PropertyLayout::default();
4440
        let particle_layout = ParticleLayout::default();
4441
        let mut ctx =
4442
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4443

4444
        let res = ctx.eval(&m, x);
4445
        assert!(res.is_ok());
4446
        let xx = res.ok().unwrap();
4447
        assert_eq!(xx, format!("particle.{}", Attribute::POSITION.name()));
4448

4449
        // Use a different context; it's invalid to reuse a mutated context, as the
4450
        // expression cache will have been generated with the wrong context.
4451
        let mut ctx =
4452
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout)
4453
                .with_attribute_pointer();
4454

4455
        let res = ctx.eval(&m, x);
4456
        assert!(res.is_ok());
4457
        let xx = res.ok().unwrap();
4458
        assert_eq!(xx, format!("(*particle).{}", Attribute::POSITION.name()));
4459
    }
4460

4461
    #[test]
4462
    #[should_panic]
4463
    fn invalid_cast_vector_to_scalar() {
4464
        let mut m = Module::default();
4465
        let x = m.lit(Vec2::ONE);
4466
        let _ = m.cast(x, ScalarType::Float);
4467
    }
4468

4469
    #[test]
4470
    #[should_panic]
4471
    fn invalid_cast_matrix_to_scalar() {
4472
        let mut m = Module::default();
4473
        let x = m.lit(Value::Matrix(Mat4::ZERO.into()));
4474
        let _ = m.cast(x, ScalarType::Float);
4475
    }
4476

4477
    #[test]
4478
    #[should_panic]
4479
    fn invalid_cast_matrix_to_vector() {
4480
        let mut m = Module::default();
4481
        let x = m.lit(Value::Matrix(Mat4::ZERO.into()));
4482
        let _ = m.cast(x, VectorType::VEC4F);
4483
    }
4484

4485
    #[test]
4486
    #[should_panic]
4487
    fn invalid_cast_scalar_to_matrix() {
4488
        let mut m = Module::default();
4489
        let x = m.lit(3.);
4490
        let _ = m.cast(x, MatrixType::MAT3X3F);
4491
    }
4492

4493
    #[test]
4494
    #[should_panic]
4495
    fn invalid_cast_vector_to_matrix() {
4496
        let mut m = Module::default();
4497
        let x = m.lit(Vec3::ZERO);
4498
        let _ = m.cast(x, MatrixType::MAT2X4F);
4499
    }
4500

4501
    #[test]
4502
    fn cast_expr_new() {
4503
        let mut m = Module::default();
4504

4505
        let x = m.attr(Attribute::POSITION);
4506
        let c = CastExpr::new(x, VectorType::VEC3F);
4507
        assert_eq!(c.value_type(), ValueType::Vector(VectorType::VEC3F));
4508
        assert_eq!(c.is_valid(&m), Some(true));
4509

4510
        let x = m.attr(Attribute::POSITION);
4511
        let c = CastExpr::new(x, ScalarType::Bool);
4512
        assert_eq!(c.value_type(), ValueType::Scalar(ScalarType::Bool));
4513
        assert_eq!(c.is_valid(&m), Some(false)); // invalid cast vector -> scalar
4514

4515
        let p = m.add_property("my_prop", 3.0.into());
4516
        let y = m.prop(p);
4517
        let c = CastExpr::new(y, MatrixType::MAT2X3F);
4518
        assert_eq!(c.value_type(), ValueType::Matrix(MatrixType::MAT2X3F));
4519
        assert_eq!(c.is_valid(&m), None); // properties' value_type() is unknown
4520
    }
4521

4522
    #[test]
4523
    fn side_effect() {
4524
        let mut m = Module::default();
4525

4526
        // Adding the same cloned expression with side effect to itself should yield
4527
        // twice the value, and not two separate evaluations of the expression.
4528
        // CORRECT:
4529
        //   let r = frand();
4530
        //   r + r
4531
        // INCORRECT:
4532
        //   frand() + frand()
4533

4534
        let r = m.builtin(BuiltInOperator::Rand(ScalarType::Float.into()));
4535
        let r2 = r;
4536
        let r3 = r2;
4537
        let a = m.add(r, r2);
4538
        let b = m.mix(r, r2, r3);
4539
        let c = m.abs(a);
4540

4541
        {
4542
            let property_layout = PropertyLayout::default();
4543
            let particle_layout = ParticleLayout::default();
4544
            let mut ctx =
4545
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4546
            let value = ctx.eval(&m, a).unwrap();
4547
            assert_eq!(value, "(var0) + (var0)");
4548
            assert_eq!(ctx.main_code, "let var0 = frand();\n");
4549
        }
4550

4551
        {
4552
            let property_layout = PropertyLayout::default();
4553
            let particle_layout = ParticleLayout::default();
4554
            let mut ctx =
4555
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4556
            let value = ctx.eval(&m, b).unwrap();
4557
            assert_eq!(value, "mix(var0, var0, var0)");
4558
            assert_eq!(ctx.main_code, "let var0 = frand();\n");
4559
        }
4560

4561
        {
4562
            let property_layout = PropertyLayout::default();
4563
            let particle_layout = ParticleLayout::default();
4564
            let mut ctx =
4565
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4566
            let value = ctx.eval(&m, c).unwrap();
4567
            assert_eq!(value, "abs((var0) + (var0))");
4568
            assert_eq!(ctx.main_code, "let var0 = frand();\n");
4569
        }
4570
    }
4571

4572
    // #[test]
4573
    // fn serde() {
4574
    //     let v = Value::Scalar(3.0_f32.into());
4575
    //     let l: LiteralExpr = v.into();
4576
    //     assert_eq!(Ok(v), l.eval());
4577
    //     let s = ron::to_string(&l).unwrap();
4578
    //     println!("literal: {:?}", s);
4579
    //     let l_serde: LiteralExpr = ron::from_str(&s).unwrap();
4580
    //     assert_eq!(l_serde, l);
4581

4582
    //     let b: ExprHandle = Box::new(l);
4583
    //     let s = ron::to_string(&b).unwrap();
4584
    //     println!("boxed literal: {:?}", s);
4585
    //     let b_serde: ExprHandle = ron::from_str(&s).unwrap();
4586
    //     assert!(b_serde.is_const());
4587
    //     assert_eq!(b_serde.to_wgsl_string(), b.to_wgsl_string());
4588

4589
    //     let v0 = Value::Scalar(3.0_f32.into());
4590
    //     let v1 = Value::Scalar(2.5_f32.into());
4591
    //     let l0: LiteralExpr = v0.into();
4592
    //     let l1: LiteralExpr = v1.into();
4593
    //     let a = l0 + l1;
4594
    //     assert!(a.is_const());
4595
    //     assert_eq!(Ok(Value::Scalar(5.5_f32.into())), a.eval());
4596
    //     let s = ron::to_string(&a).unwrap();
4597
    //     println!("add: {:?}", s);
4598
    //     let a_serde: AddExpr = ron::from_str(&s).unwrap();
4599
    //     println!("a_serde: {:?}", a_serde);
4600
    //     assert_eq!(a_serde.left.to_wgsl_string(), l0.to_wgsl_string());
4601
    //     assert_eq!(a_serde.right.to_wgsl_string(), l1.to_wgsl_string());
4602
    // }
4603
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc