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

djeedai / bevy_hanabi / 17957503606

23 Sep 2025 07:56PM UTC coverage: 66.455% (+0.2%) from 66.288%
17957503606

push

github

web-flow
Check the results of `has_side_effect` for binary expressions. (#497)

Currently, if the output of `UniformRand` is used in multiple dependent
expressions, a new random number will be generated every time. This is
at odds with the behavior of `BuiltInOperator::Rand`. The code *tries*
to account for this by implementing `has_side_effect()` on binary
expressions, but this is never actually checked. That's because
`has_side_effect()` is only examined for *built-in* expressions, which
`UniformRand` is not one of.

This commit factors the logic to create a local variable if there are no
side effects out of `BuiltInExpr` into a separate function and calls
that function in `Expr::Binary` and `BuiltInExpr`, fixing the issue.

A separate issue is that `Expr::has_side_effect()` is perhaps too
aggressive, in that binary operators check to see whether their LHS and
RHS are side-effecting and mark themselves as side-effecting if they
are, when in fact only the operator itself determines whether the
expression is side-effecting for the purposes of generating new
temporaries. This causes many expressions to be marked as side-effecting
that actually aren't. I opted not to fix this issue in this PR to keep
this PR small, as it only results in more temporaries being generated,
which shader compilers will clean up.

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

1 existing line in 1 file now uncovered.

4917 of 7399 relevant lines covered (66.45%)

449.07 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