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

djeedai / bevy_hanabi / 18428735982

11 Oct 2025 11:20AM UTC coverage: 66.593% (+0.01%) from 66.58%
18428735982

push

github

web-flow
Add missing `clamp` and `vec3` expressions (#504)

- Add missing `clamp(x, lo, hi)` expression.
- Expose `vec3(x, y, z)` to `Module`; it was only available in
`WriterExpr` by mistake.
- Same for `vec4_xyz_w(xyz, w)`.
- Add tests for those.

1 of 5 new or added lines in 1 file covered. (20.0%)

2 existing lines in 1 file now uncovered.

5117 of 7684 relevant lines covered (66.59%)

148.05 hits per line

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

54.77
/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 {
249✔
147
        debug_assert!(id != 0);
498✔
148
        Self {
149
            id: NonZeroU32::new_unchecked(id as u32),
249✔
150
        }
151
    }
152

153
    /// Get the zero-based index into the array of the module.
154
    fn index(&self) -> usize {
422✔
155
        (self.id.get() - 1) as usize
422✔
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 {
35✔
282
            self.binary(BinaryOperator::$T, left, right)
175✔
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 {
5✔
292
            self.ternary(TernaryOperator::$T, first, second, third)
30✔
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] {
326✔
356
        &self.properties
326✔
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>) {
648✔
398
        for expr in &self.expressions {
3,144✔
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 {
248✔
407
        self.expressions.push(expr.into());
992✔
408
        #[allow(unsafe_code)]
×
409
        unsafe {
410
            ExprHandle::new_unchecked(self.expressions.len())
744✔
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
83✔
417
    where
418
        Value: From<V>,
419
    {
420
        self.push(Expr::Literal(LiteralExpr::new(value)))
249✔
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(
35✔
519
        &mut self,
520
        op: BinaryOperator,
521
        left: ExprHandle,
522
        right: ExprHandle,
523
    ) -> ExprHandle {
524
        assert!(left.index() < self.expressions.len());
140✔
525
        assert!(right.index() < self.expressions.len());
140✔
526
        self.push(Expr::Binary { op, left, right })
140✔
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
    impl_module_binary!(vec4_xyz_w, Vec4XyzW);
549

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

584
    impl_module_ternary!(mix, Mix);
585
    impl_module_ternary!(clamp, Clamp);
586
    impl_module_ternary!(smoothstep, SmoothStep);
587
    impl_module_ternary!(vec3, Vec3);
588

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

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

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

631
    /// Get an existing expression from its handle.
632
    #[inline]
633
    pub fn try_get(&self, expr: ExprHandle) -> Result<&Expr, ExprError> {
272✔
634
        let index = expr.index();
816✔
635
        self.expressions
544✔
636
            .get(index)
272✔
637
            .ok_or(ExprError::InvalidExprHandleError(format!(
544✔
638
                "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)))
272✔
639
    }
640

641
    /// Get an existing expression from its handle.
642
    #[inline]
643
    pub fn try_get_mut(&mut self, expr: ExprHandle) -> Result<&mut Expr, ExprError> {
2✔
644
        let index = expr.index();
6✔
645
        self.expressions
4✔
646
            .get_mut(index)
2✔
647
            .ok_or(ExprError::InvalidExprHandleError(format!(
4✔
648
                "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✔
649
    }
650

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

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

677
    /// Get the texture layout of this module.
678
    pub fn texture_layout(&self) -> TextureLayout {
342✔
679
        self.texture_layout.clone()
684✔
680
    }
681
}
682

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

695
    /// Expression syntax error.
696
    #[error("Syntax error: {0}")]
697
    SyntaxError(String),
698

699
    /// Generic graph evaluation error.
700
    #[error("Graph evaluation error: {0}")]
701
    GraphEvalError(String),
702

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

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

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

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

746
    /// Get the particle layout of the effect.
747
    fn particle_layout(&self) -> &ParticleLayout;
748

749
    /// Get the property layout of the effect.
750
    fn property_layout(&self) -> &PropertyLayout;
751

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

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

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

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

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

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

820
    /// Literal expression ([`LiteralExpr`]).
821
    ///
822
    /// A literal expression represents a shader constants.
823
    Literal(LiteralExpr),
824

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

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

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

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

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

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

888
    /// Cast expression.
889
    ///
890
    /// An expression to cast an expression to another type.
891
    Cast(CastExpr),
892

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

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

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

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

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

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

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

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

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

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

1119
                body.map(|body| {
40✔
1120
                    check_side_effects_and_create_local_if_needed(
40✔
1121
                        context,
40✔
1122
                        body,
40✔
1123
                        self.has_side_effect(module),
120✔
1124
                    )
1125
                })
1126
            }
1127
            Expr::Ternary {
1128
                op,
5✔
1129
                first,
5✔
1130
                second,
5✔
1131
                third,
5✔
1132
            } => {
1133
                // Recursively evaluate child expressions throught the context to ensure caching
1134
                let first = context.eval(module, *first)?;
25✔
1135
                let second = context.eval(module, *second)?;
5✔
1136
                let third = context.eval(module, *third)?;
5✔
1137

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

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

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

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

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

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

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

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

1205
impl ToWgslString for LiteralExpr {
1206
    fn to_wgsl_string(&self) -> String {
×
1207
        self.value.to_wgsl_string()
×
1208
    }
1209
}
1210

1211
impl From<&Value> for LiteralExpr {
1212
    fn from(value: &Value) -> Self {
×
1213
        Self { value: *value }
×
1214
    }
1215
}
1216

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

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

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

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

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

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

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

1283
impl From<Attribute> for AttributeExpr {
1284
    fn from(value: Attribute) -> Self {
×
1285
        AttributeExpr::new(value)
×
1286
    }
1287
}
1288

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

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

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

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

1332
        Ok(format!("properties.{}", prop.name()))
6✔
1333
    }
1334
}
1335

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1676
impl ToWgslString for BuiltInExpr {
1677
    fn to_wgsl_string(&self) -> String {
37✔
1678
        self.operator.to_wgsl_string()
74✔
1679
    }
1680
}
1681

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

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

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

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

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

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

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

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

1751
    /// Cosine operator.
1752
    Cos,
1753

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

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

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

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

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

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

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

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

1806
    /// Vector normalizing operator.
1807
    ///
1808
    /// Normalize the given numeric vector. Only valid for numeric vector
1809
    /// operands.
1810
    Normalize,
1811

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

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

1828
    /// Rounding operator.
1829
    ///
1830
    /// Round the given scalar of vector float to the nearest integer, returned
1831
    /// as a float value.
1832
    Round,
1833

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

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

1851
    /// Sine operator.
1852
    Sin,
1853

1854
    /// Square root operator.
1855
    ///
1856
    /// Return the square root of the floating-point operand, component-wise for
1857
    /// vectors.
1858
    Sqrt,
1859

1860
    /// Tangent operator.
1861
    Tan,
1862

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

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

1876
    /// Get the fourth component of a vector.
1877
    ///
1878
    /// This is only valid for vectors of rank 4.
1879
    W,
1880

1881
    /// Get the first component of a scalar or vector.
1882
    ///
1883
    /// For scalar, return the value itself. For vectors, return the first
1884
    /// component.
1885
    X,
1886

1887
    /// Get the second component of a vector.
1888
    Y,
1889

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

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

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

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

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

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

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

1980
    /// Division operator.
1981
    ///
1982
    /// Returns the left operand divided by the right operand. Only valid for
1983
    /// numeric operands.
1984
    Div,
1985

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

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

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

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

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

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

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

2040
    /// Multiply operator.
2041
    ///
2042
    /// Returns the product of its operands. Only valid for numeric operands.
2043
    Mul,
2044

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

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

2060
    /// Subtraction operator.
2061
    ///
2062
    /// Returns the difference between its left and right operands. Only valid
2063
    /// for numeric operands.
2064
    Sub,
2065

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

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

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

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

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

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

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

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

2193
    /// Clamp operator.
2194
    ///
2195
    /// Returns the first argument clamped to the range defined by the second
2196
    /// and third arguments. If the operands are vectors, they must be of
2197
    /// the same rank, and the result is a vector of that rank
2198
    /// and same element scalar type.
2199
    ///
2200
    /// The expression `clamp(x, low, high)` is equivalent to `min(max(x, low),
2201
    /// high)`.
2202
    Clamp,
2203

2204
    /// Smooth stepping operator.
2205
    ///
2206
    /// Returns the smooth Hermitian interpolation between the first and second
2207
    /// argument, calculated at the third argument. If the operands are vectors,
2208
    /// they must be of the same rank, and the result is a vector of that
2209
    /// rank and same element scalar type.
2210
    ///
2211
    /// The smooth stepping of `low` and `high` at position `x` is equivalent to
2212
    /// `t * t * (3. - 2. * t)` where `t = clamp((x - low) / (high - low))`
2213
    /// represents the fractional position of `x` between `low` and `high`.
2214
    ///
2215
    /// The result is always a floating point scalar in \[0:1\].
2216
    SmoothStep,
2217

2218
    /// Constructor for 3-element vectors.
2219
    ///
2220
    /// Given three scalar elements `x`, `y`, and `z`, returns the vector
2221
    /// consisting of those three elements `(x, y, z)`.
2222
    Vec3,
2223
}
2224

2225
impl ToWgslString for TernaryOperator {
2226
    fn to_wgsl_string(&self) -> String {
5✔
2227
        match *self {
5✔
2228
            TernaryOperator::Mix => "mix".to_string(),
2✔
2229
            TernaryOperator::Clamp => "clamp".to_string(),
2✔
2230
            TernaryOperator::SmoothStep => "smoothstep".to_string(),
2✔
2231
            TernaryOperator::Vec3 => "vec3".to_string(),
2✔
2232
        }
2233
    }
2234
}
2235

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

2279
#[allow(dead_code)]
2280
impl ExprWriter {
2281
    /// Create a new writer.
2282
    ///
2283
    /// The writer owns a new [`Module`] internally, and write all expressions
2284
    /// to it. The module can be released to the user with [`finish()`] once
2285
    /// done using the writer.
2286
    ///
2287
    /// [`finish()`]: ExprWriter::finish
2288
    pub fn new() -> Self {
3✔
2289
        Self {
2290
            module: Rc::new(RefCell::new(Module::default())),
6✔
2291
        }
2292
    }
2293

2294
    /// Create a new writer from an existing module.
2295
    ///
2296
    /// This is an advanced use entry point to write expressions into an
2297
    /// existing [`Module`]. In general, users should prefer using
2298
    /// [`ExprWriter::new()`] to create a new [`Module`], and keep using the
2299
    /// same [`ExprWriter`] to write all expressions of the same
2300
    /// [`EffectAsset`].
2301
    ///
2302
    /// [`EffectAsset`]: crate::EffectAsset
2303
    pub fn from_module(module: Rc<RefCell<Module>>) -> Self {
×
2304
        Self { module }
2305
    }
2306

2307
    /// Add a new property.
2308
    ///
2309
    /// See [`Property`] for more details on what effect properties are.
2310
    ///
2311
    /// # Panics
2312
    ///
2313
    /// Panics if a property with the same name already exists.
2314
    pub fn add_property(&self, name: impl Into<String>, default_value: Value) -> PropertyHandle {
1✔
2315
        self.module.borrow_mut().add_property(name, default_value)
3✔
2316
    }
2317

2318
    /// Push a new expression into the writer.
2319
    pub fn push(&self, expr: impl Into<Expr>) -> WriterExpr {
13✔
2320
        let expr = {
13✔
2321
            let mut m = self.module.borrow_mut();
26✔
2322
            m.push(expr.into())
39✔
2323
        };
2324
        WriterExpr {
2325
            expr,
2326
            module: Rc::clone(&self.module),
13✔
2327
        }
2328
    }
2329

2330
    /// Create a new writer expression from a literal constant.
2331
    ///
2332
    /// # Example
2333
    ///
2334
    /// ```
2335
    /// # use bevy_hanabi::*;
2336
    /// let mut w = ExprWriter::new();
2337
    /// let x = w.lit(-3.5); // x = -3.5;
2338
    /// ```
2339
    pub fn lit(&self, value: impl Into<Value>) -> WriterExpr {
11✔
2340
        self.push(Expr::Literal(LiteralExpr {
33✔
2341
            value: value.into(),
11✔
2342
        }))
2343
    }
2344

2345
    /// Create a new writer expression from an attribute.
2346
    ///
2347
    /// # Example
2348
    ///
2349
    /// ```
2350
    /// # use bevy_hanabi::*;
2351
    /// let mut w = ExprWriter::new();
2352
    /// let x = w.attr(Attribute::POSITION); // x = particle.position;
2353
    /// ```
2354
    pub fn attr(&self, attr: Attribute) -> WriterExpr {
1✔
2355
        self.push(Expr::Attribute(AttributeExpr::new(attr)))
3✔
2356
    }
2357

2358
    /// Create a new writer expression from an attribute on a parent effect.
2359
    ///
2360
    /// # Example
2361
    ///
2362
    /// ```
2363
    /// # use bevy_hanabi::*;
2364
    /// let mut w = ExprWriter::new();
2365
    /// let x = w.parent_attr(Attribute::POSITION); // x = parent_particle.position;
2366
    /// ```
2367
    pub fn parent_attr(&self, attr: Attribute) -> WriterExpr {
×
2368
        self.push(Expr::ParentAttribute(AttributeExpr::new(attr)))
×
2369
    }
2370

2371
    /// Create a new writer expression from a property.
2372
    ///
2373
    /// # Example
2374
    ///
2375
    /// ```
2376
    /// # use bevy_hanabi::*;
2377
    /// let mut w = ExprWriter::new();
2378
    /// let prop = w.add_property("my_prop", 3.0.into());
2379
    /// let x = w.prop(prop); // x = properties.my_prop;
2380
    /// ```
2381
    pub fn prop(&self, handle: PropertyHandle) -> WriterExpr {
1✔
2382
        self.push(Expr::Property(PropertyExpr::new(handle)))
3✔
2383
    }
2384

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

2398
    /// Create a new writer expression representing the simulation delta time
2399
    /// since last frame.
2400
    ///
2401
    /// # Example
2402
    ///
2403
    /// ```
2404
    /// # use bevy_hanabi::*;
2405
    /// let mut w = ExprWriter::new();
2406
    /// let x = w.delta_time(); // x = sim_params.delta_time;
2407
    /// ```
2408
    pub fn delta_time(&self) -> WriterExpr {
×
2409
        self.push(Expr::BuiltIn(BuiltInExpr::new(BuiltInOperator::DeltaTime)))
×
2410
    }
2411

2412
    /// Create a new writer expression representing a random value of the given
2413
    /// type.
2414
    ///
2415
    /// The type can be any scalar or vector type. Matrix types are not
2416
    /// supported. The random values generated are uniformly distributed in
2417
    /// `[0:1]`. For vectors, each component is sampled separately.
2418
    ///
2419
    /// # Panics
2420
    ///
2421
    /// Panics in the same cases as [`BuiltInExpr::new()`] does.
2422
    ///
2423
    /// # Example
2424
    ///
2425
    /// ```
2426
    /// # use bevy_hanabi::*;
2427
    /// let mut w = ExprWriter::new();
2428
    /// let x = w.rand(VectorType::VEC3F); // x = frand3();
2429
    /// ```
2430
    pub fn rand(&self, value_type: impl Into<ValueType>) -> WriterExpr {
×
2431
        self.push(Expr::BuiltIn(BuiltInExpr::new(BuiltInOperator::Rand(
×
2432
            value_type.into(),
×
2433
        ))))
2434
    }
2435

2436
    /// Create a new writer expression representing the alpha cutoff value used
2437
    /// for alpha masking.
2438
    ///
2439
    /// This expression is only valid when used in the context of the fragment
2440
    /// shader, in the render context.
2441
    ///
2442
    /// # Example
2443
    ///
2444
    /// ```
2445
    /// # use bevy_hanabi::*;
2446
    /// let mut w = ExprWriter::new();
2447
    /// let x = w.alpha_cutoff(); // x = alpha_cutoff;
2448
    /// ```
2449
    pub fn alpha_cutoff(&self) -> WriterExpr {
×
2450
        self.push(Expr::BuiltIn(BuiltInExpr::new(
×
2451
            BuiltInOperator::AlphaCutoff,
×
2452
        )))
2453
    }
2454

2455
    /// Finish using the writer, and recover the [`Module`] where all [`Expr`]
2456
    /// were written by the writer.
2457
    ///
2458
    /// This module is typically passed to [`EffectAsset::new()`] before adding
2459
    /// to that effect the modifiers which use the expressions created by this
2460
    /// writer.
2461
    ///
2462
    /// # Example
2463
    ///
2464
    /// ```
2465
    /// # use bevy_hanabi::*;
2466
    /// # let spawner = SpawnerSettings::default();
2467
    /// let mut w = ExprWriter::new();
2468
    /// // [...]
2469
    /// let module = w.finish();
2470
    /// let asset = EffectAsset::new(256, spawner, module);
2471
    /// ```
2472
    ///
2473
    /// [`EffectAsset::new()`]: crate::EffectAsset::new()
2474
    pub fn finish(self) -> Module {
3✔
2475
        self.module.take()
3✔
2476
    }
2477
}
2478

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

2524
impl WriterExpr {
2525
    fn unary_op(self, op: UnaryOperator) -> Self {
1✔
2526
        let expr = self.module.borrow_mut().push(Expr::Unary {
4✔
2527
            op,
1✔
2528
            expr: self.expr,
1✔
2529
        });
2530
        WriterExpr {
2531
            expr,
2532
            module: self.module,
1✔
2533
        }
2534
    }
2535

2536
    /// Take the absolute value of the current expression.
2537
    ///
2538
    /// This is a unary operator, which applies component-wise to vector and
2539
    /// matrix operand expressions.
2540
    ///
2541
    /// # Example
2542
    ///
2543
    /// ```
2544
    /// # use bevy_hanabi::*;
2545
    /// # let mut w = ExprWriter::new();
2546
    /// // A literal expression `x = -3.5;`.
2547
    /// let x = w.lit(-3.5);
2548
    ///
2549
    /// // The absolute value `y = abs(x);`.
2550
    /// let y = x.abs(); // == 3.5
2551
    /// ```
2552
    #[inline]
2553
    pub fn abs(self) -> Self {
1✔
2554
        self.unary_op(UnaryOperator::Abs)
3✔
2555
    }
2556

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

2579
    /// Apply the logical operator "any" to the current bool vector expression.
2580
    ///
2581
    /// This is a unary operator, which applies to vector operand expressions to
2582
    /// produce a scalar boolean.
2583
    ///
2584
    /// # Example
2585
    ///
2586
    /// ```
2587
    /// # use bevy_hanabi::*;
2588
    /// # use bevy::math::BVec3;
2589
    /// # let mut w = ExprWriter::new();
2590
    /// // A literal expression `x = vec3<bool>(true, false, true);`.
2591
    /// let x = w.lit(BVec3::new(true, false, true));
2592
    ///
2593
    /// // Check if any components is true `y = any(x);`.
2594
    /// let y = x.any(); // == true
2595
    /// ```
2596
    #[inline]
2597
    pub fn any(self) -> Self {
×
2598
        self.unary_op(UnaryOperator::Any)
×
2599
    }
2600

2601
    /// Apply the "acos" (inverse cosine) operator to the current float scalar
2602
    /// or vector expression.
2603
    ///
2604
    /// This is a unary operator, which applies to float scalar or vector
2605
    /// operand expressions to produce a float scalar or vector. It applies
2606
    /// component-wise to vector operand expressions. The return value lies in
2607
    /// the 0 ≤ x ≤ π range.
2608
    ///
2609
    /// # Example
2610
    ///
2611
    /// ```
2612
    /// # use bevy_hanabi::*;
2613
    /// # use bevy::math::Vec3;
2614
    /// # let mut w = ExprWriter::new();
2615
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2616
    /// let x = w.lit(Vec3::ONE);
2617
    ///
2618
    /// // Acos: `y = acos(x);`
2619
    /// let y = x.acos();
2620
    /// ```
2621
    #[inline]
2622
    pub fn acos(self) -> Self {
×
2623
        self.unary_op(UnaryOperator::Acos)
×
2624
    }
2625

2626
    /// Apply the "asin" (inverse sine) operator to the current float scalar or
2627
    /// vector expression.
2628
    ///
2629
    /// This is a unary operator, which applies to float scalar or vector
2630
    /// operand expressions to produce a float scalar or vector. It applies
2631
    /// component-wise to vector operand expressions. The return value lies in
2632
    /// the -π/2 ≤ x ≤ π/2 range.
2633
    ///
2634
    /// # Example
2635
    ///
2636
    /// ```
2637
    /// # use bevy_hanabi::*;
2638
    /// # use bevy::math::Vec3;
2639
    /// # let mut w = ExprWriter::new();
2640
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2641
    /// let x = w.lit(Vec3::ONE);
2642
    ///
2643
    /// // Asin: `y = asin(x);`
2644
    /// let y = x.asin();
2645
    /// ```
2646
    #[inline]
2647
    pub fn asin(self) -> Self {
×
2648
        self.unary_op(UnaryOperator::Asin)
×
2649
    }
2650

2651
    /// Apply the "atan" (inverse tangent) operator to the current float scalar
2652
    /// or vector expression.
2653
    ///
2654
    /// This is a unary operator, which applies to float scalar or vector
2655
    /// operand expressions to produce a float scalar or vector. It applies
2656
    /// component-wise to vector operand expressions. The return value lies in
2657
    /// the -π/2 ≤ x ≤ π/2 range.
2658
    ///
2659
    /// # Example
2660
    ///
2661
    /// ```
2662
    /// # use bevy_hanabi::*;
2663
    /// # use bevy::math::Vec3;
2664
    /// # let mut w = ExprWriter::new();
2665
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2666
    /// let x = w.lit(Vec3::ONE);
2667
    ///
2668
    /// // Atan: `y = atan(x);`
2669
    /// let y = x.atan();
2670
    /// ```
2671
    #[inline]
2672
    pub fn atan(self) -> Self {
×
2673
        self.unary_op(UnaryOperator::Atan)
×
2674
    }
2675

2676
    /// Apply the "ceil" operator to the current float scalar or vector
2677
    /// expression.
2678
    ///
2679
    /// This is a unary operator, which applies to float scalar or vector
2680
    /// operand expressions to produce a float scalar or vector. It applies
2681
    /// component-wise to vector operand expressions.
2682
    ///
2683
    /// # Example
2684
    ///
2685
    /// ```
2686
    /// # use bevy_hanabi::*;
2687
    /// # use bevy::math::Vec3;
2688
    /// # let mut w = ExprWriter::new();
2689
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2690
    /// let x = w.lit(Vec3::ONE);
2691
    ///
2692
    /// // Ceil: `y = ceil(x);`
2693
    /// let y = x.ceil();
2694
    /// ```
2695
    #[inline]
2696
    pub fn ceil(self) -> Self {
×
2697
        self.unary_op(UnaryOperator::Ceil)
×
2698
    }
2699

2700
    /// Apply the "cos" operator to the current float scalar or vector
2701
    /// expression.
2702
    ///
2703
    /// This is a unary operator, which applies to float scalar or vector
2704
    /// operand expressions to produce a float scalar or vector. It applies
2705
    /// component-wise to vector operand expressions.
2706
    ///
2707
    /// # Example
2708
    ///
2709
    /// ```
2710
    /// # use bevy_hanabi::*;
2711
    /// # use bevy::math::Vec3;
2712
    /// # let mut w = ExprWriter::new();
2713
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2714
    /// let x = w.lit(Vec3::ONE);
2715
    ///
2716
    /// // Cos: `y = cos(x);`
2717
    /// let y = x.cos();
2718
    /// ```
2719
    #[inline]
2720
    pub fn cos(self) -> Self {
×
2721
        self.unary_op(UnaryOperator::Cos)
×
2722
    }
2723

2724
    /// Apply the "exp" operator to the current float scalar or vector
2725
    /// expression.
2726
    ///
2727
    /// This is a unary operator, which applies to float scalar or vector
2728
    /// operand expressions to produce a float scalar or vector. It applies
2729
    /// component-wise to vector operand expressions.
2730
    ///
2731
    /// # Example
2732
    ///
2733
    /// ```
2734
    /// # use bevy_hanabi::*;
2735
    /// # use bevy::math::Vec3;
2736
    /// # let mut w = ExprWriter::new();
2737
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2738
    /// let x = w.lit(Vec3::ONE);
2739
    ///
2740
    /// // Exp: `y = exp(x);`
2741
    /// let y = x.exp();
2742
    /// ```
2743
    #[inline]
2744
    pub fn exp(self) -> Self {
×
2745
        self.unary_op(UnaryOperator::Exp)
×
2746
    }
2747

2748
    /// Apply the "exp2" operator to the current float scalar or vector
2749
    /// expression.
2750
    ///
2751
    /// This is a unary operator, which applies to float scalar or vector
2752
    /// operand expressions to produce a float scalar or vector. It applies
2753
    /// component-wise to vector operand expressions.
2754
    ///
2755
    /// # Example
2756
    ///
2757
    /// ```
2758
    /// # use bevy_hanabi::*;
2759
    /// # use bevy::math::Vec3;
2760
    /// # let mut w = ExprWriter::new();
2761
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2762
    /// let x = w.lit(Vec3::ONE);
2763
    ///
2764
    /// // Exp2: `y = exp2(x);`
2765
    /// let y = x.exp2();
2766
    /// ```
2767
    #[inline]
2768
    pub fn exp2(self) -> Self {
×
2769
        self.unary_op(UnaryOperator::Exp2)
×
2770
    }
2771

2772
    /// Apply the "floor" operator to the current float scalar or vector
2773
    /// expression.
2774
    ///
2775
    /// This is a unary operator, which applies to float scalar or vector
2776
    /// operand expressions to produce a float scalar or vector. It applies
2777
    /// component-wise to vector operand expressions.
2778
    ///
2779
    /// # Example
2780
    ///
2781
    /// ```
2782
    /// # use bevy_hanabi::*;
2783
    /// # use bevy::math::Vec3;
2784
    /// # let mut w = ExprWriter::new();
2785
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2786
    /// let x = w.lit(Vec3::ONE);
2787
    ///
2788
    /// // Floor: `y = floor(x);`
2789
    /// let y = x.floor();
2790
    /// ```
2791
    #[inline]
2792
    pub fn floor(self) -> Self {
×
2793
        self.unary_op(UnaryOperator::Floor)
×
2794
    }
2795

2796
    /// Apply the "fract" operator to the current float scalar or vector
2797
    /// expression.
2798
    ///
2799
    /// This is a unary operator, which applies to float scalar or vector
2800
    /// operand expressions to produce a float scalar or vector. It applies
2801
    /// component-wise to vector operand expressions.
2802
    ///
2803
    /// # Example
2804
    ///
2805
    /// ```
2806
    /// # use bevy_hanabi::*;
2807
    /// # use bevy::math::Vec3;
2808
    /// # let mut w = ExprWriter::new();
2809
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2810
    /// let x = w.lit(Vec3::ONE);
2811
    ///
2812
    /// // Fract: `y = fract(x);`
2813
    /// let y = x.fract();
2814
    /// ```
2815
    #[inline]
2816
    pub fn fract(self) -> Self {
×
2817
        self.unary_op(UnaryOperator::Fract)
×
2818
    }
2819

2820
    /// Apply the "inverseSqrt" (inverse square root) operator to the current
2821
    /// float scalar or vector expression.
2822
    ///
2823
    /// This is a unary operator, which applies to float scalar or vector
2824
    /// operand expressions to produce a float scalar or vector. It applies
2825
    /// component-wise to vector operand expressions.
2826
    ///
2827
    /// # Example
2828
    ///
2829
    /// ```
2830
    /// # use bevy_hanabi::*;
2831
    /// # use bevy::math::Vec3;
2832
    /// # let mut w = ExprWriter::new();
2833
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2834
    /// let x = w.lit(Vec3::ONE);
2835
    ///
2836
    /// // Inverse square root: `y = inverseSqrt(x) = 1.0 / sqrt(x);`
2837
    /// let y = x.inverse_sqrt();
2838
    /// ```
2839
    #[inline]
2840
    pub fn inverse_sqrt(self) -> Self {
×
2841
        self.unary_op(UnaryOperator::InvSqrt)
×
2842
    }
2843

2844
    /// Apply the "length" operator to the current float scalar or vector
2845
    /// expression.
2846
    ///
2847
    /// This is a unary operator, which applies to float scalar or vector
2848
    /// operand expressions to produce a float scalar or vector. It applies
2849
    /// component-wise to vector operand expressions.
2850
    ///
2851
    /// # Example
2852
    ///
2853
    /// ```
2854
    /// # use bevy_hanabi::*;
2855
    /// # use bevy::math::Vec3;
2856
    /// # let mut w = ExprWriter::new();
2857
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2858
    /// let x = w.lit(Vec3::ONE);
2859
    ///
2860
    /// // Length: `y = length(x);`
2861
    /// let y = x.length();
2862
    /// ```
2863
    #[inline]
2864
    pub fn length(self) -> Self {
×
2865
        self.unary_op(UnaryOperator::Length)
×
2866
    }
2867

2868
    /// Apply the "log" operator to the current float scalar or vector
2869
    /// expression.
2870
    ///
2871
    /// This is a unary operator, which applies to float scalar or vector
2872
    /// operand expressions to produce a float scalar or vector. It applies
2873
    /// component-wise to vector operand expressions.
2874
    ///
2875
    /// # Example
2876
    ///
2877
    /// ```
2878
    /// # use bevy_hanabi::*;
2879
    /// # use bevy::math::Vec3;
2880
    /// # let mut w = ExprWriter::new();
2881
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2882
    /// let x = w.lit(Vec3::ONE);
2883
    ///
2884
    /// // Log: `y = log(x);`
2885
    /// let y = x.log();
2886
    /// ```
2887
    #[inline]
2888
    pub fn log(self) -> Self {
×
2889
        self.unary_op(UnaryOperator::Log)
×
2890
    }
2891

2892
    /// Apply the "log2" operator to the current float scalar or vector
2893
    /// expression.
2894
    ///
2895
    /// This is a unary operator, which applies to float scalar or vector
2896
    /// operand expressions to produce a float scalar or vector. It applies
2897
    /// component-wise to vector operand expressions.
2898
    ///
2899
    /// # Example
2900
    ///
2901
    /// ```
2902
    /// # use bevy_hanabi::*;
2903
    /// # use bevy::math::Vec3;
2904
    /// # let mut w = ExprWriter::new();
2905
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2906
    /// let x = w.lit(Vec3::ONE);
2907
    ///
2908
    /// // Log2: `y = log2(x);`
2909
    /// let y = x.log2();
2910
    /// ```
2911
    #[inline]
2912
    pub fn log2(self) -> Self {
×
2913
        self.unary_op(UnaryOperator::Log2)
×
2914
    }
2915

2916
    /// Apply the "normalize" operator to the current float vector expression.
2917
    ///
2918
    /// This is a unary operator, which applies to float vector operand
2919
    /// expressions to produce another float vector with unit length
2920
    /// (normalized).
2921
    ///
2922
    /// # Example
2923
    ///
2924
    /// ```
2925
    /// # use bevy_hanabi::*;
2926
    /// # use bevy::math::Vec3;
2927
    /// # let mut w = ExprWriter::new();
2928
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
2929
    /// let x = w.lit(Vec3::ONE);
2930
    ///
2931
    /// // Normalize: `y = normalize(x);`
2932
    /// let y = x.normalized();
2933
    /// ```
2934
    #[inline]
2935
    pub fn normalized(self) -> Self {
×
2936
        self.unary_op(UnaryOperator::Normalize)
×
2937
    }
2938

2939
    /// Apply the "pack4x8snorm" operator to the current 4-component float
2940
    /// vector expression.
2941
    ///
2942
    /// This is a unary operator, which applies to 4-component float vector
2943
    /// operand expressions to produce a single `u32` scalar expression.
2944
    ///
2945
    /// # Example
2946
    ///
2947
    /// ```
2948
    /// # use bevy_hanabi::*;
2949
    /// # use bevy::math::Vec4;
2950
    /// # let mut w = ExprWriter::new();
2951
    /// // A literal expression `x = vec4<f32>(-1., 1., 0., 7.2);`.
2952
    /// let x = w.lit(Vec4::new(-1., 1., 0., 7.2));
2953
    ///
2954
    /// // Pack: `y = pack4x8snorm(x);`
2955
    /// let y = x.pack4x8snorm(); // 0x7F007FFFu32
2956
    /// ```
2957
    #[inline]
2958
    pub fn pack4x8snorm(self) -> Self {
×
2959
        self.unary_op(UnaryOperator::Pack4x8snorm)
×
2960
    }
2961

2962
    /// Apply the "pack4x8unorm" operator to the current 4-component float
2963
    /// vector expression.
2964
    ///
2965
    /// This is a unary operator, which applies to 4-component float vector
2966
    /// operand expressions to produce a single `u32` scalar expression.
2967
    ///
2968
    /// # Example
2969
    ///
2970
    /// ```
2971
    /// # use bevy_hanabi::*;
2972
    /// # use bevy::math::Vec4;
2973
    /// # let mut w = ExprWriter::new();
2974
    /// // A literal expression `x = vec4<f32>(-1., 1., 0., 7.2);`.
2975
    /// let x = w.lit(Vec4::new(-1., 1., 0., 7.2));
2976
    ///
2977
    /// // Pack: `y = pack4x8unorm(x);`
2978
    /// let y = x.pack4x8unorm(); // 0xFF00FF00u32
2979
    /// ```
2980
    #[inline]
2981
    pub fn pack4x8unorm(self) -> Self {
×
2982
        self.unary_op(UnaryOperator::Pack4x8unorm)
×
2983
    }
2984

2985
    /// Apply the "round" operator to the current float scalar or vector
2986
    /// expression.
2987
    ///
2988
    /// This is a unary operator, which applies to float scalar or vector
2989
    /// operand expressions to produce a float scalar or vector. It applies
2990
    /// component-wise to vector operand expressions.
2991
    ///
2992
    /// # Example
2993
    ///
2994
    /// ```
2995
    /// # use bevy_hanabi::*;
2996
    /// # use bevy::math::Vec3;
2997
    /// # let mut w = ExprWriter::new();
2998
    /// // A literal expression `x = vec3<f32>(1.5, -3.2, 0.75);`.
2999
    /// let x = w.lit(Vec3::new(1.5, -3.2, 0.75));
3000
    ///
3001
    /// // Sign: `y = round(x);`
3002
    /// let y = x.round();
3003
    /// ```
3004
    #[inline]
3005
    pub fn round(self) -> Self {
×
3006
        self.unary_op(UnaryOperator::Round)
×
3007
    }
3008

3009
    /// Apply the "sign" operator to the current float scalar or vector
3010
    /// expression.
3011
    ///
3012
    /// This is a unary operator, which applies to float scalar or vector
3013
    /// operand expressions to produce a float scalar or vector. It applies
3014
    /// component-wise to vector operand expressions.
3015
    ///
3016
    /// # Example
3017
    ///
3018
    /// ```
3019
    /// # use bevy_hanabi::*;
3020
    /// # use bevy::math::Vec3;
3021
    /// # let mut w = ExprWriter::new();
3022
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3023
    /// let x = w.lit(Vec3::ONE);
3024
    ///
3025
    /// // Sign: `y = sign(x);`
3026
    /// let y = x.sign();
3027
    /// ```
3028
    #[inline]
3029
    pub fn sign(self) -> Self {
×
3030
        self.unary_op(UnaryOperator::Sign)
×
3031
    }
3032

3033
    /// Apply the "sin" operator to the current float scalar or vector
3034
    /// expression.
3035
    ///
3036
    /// This is a unary operator, which applies to float scalar or vector
3037
    /// operand expressions to produce a float scalar or vector. It applies
3038
    /// component-wise to vector operand expressions.
3039
    ///
3040
    /// # Example
3041
    ///
3042
    /// ```
3043
    /// # use bevy_hanabi::*;
3044
    /// # use bevy::math::Vec3;
3045
    /// # let mut w = ExprWriter::new();
3046
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3047
    /// let x = w.lit(Vec3::ONE);
3048
    ///
3049
    /// // Sin: `y = sin(x);`
3050
    /// let y = x.sin();
3051
    /// ```
3052
    #[inline]
3053
    pub fn sin(self) -> Self {
×
3054
        self.unary_op(UnaryOperator::Sin)
×
3055
    }
3056

3057
    /// Apply the "sqrt" (square root) operator to the current float scalar or
3058
    /// vector expression.
3059
    ///
3060
    /// This is a unary operator, which applies to float scalar or vector
3061
    /// operand expressions to produce a float scalar or vector. It applies
3062
    /// component-wise to vector operand expressions.
3063
    ///
3064
    /// # Example
3065
    ///
3066
    /// ```
3067
    /// # use bevy_hanabi::*;
3068
    /// # use bevy::math::Vec3;
3069
    /// # let mut w = ExprWriter::new();
3070
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3071
    /// let x = w.lit(Vec3::ONE);
3072
    ///
3073
    /// // Square root: `y = sqrt(x);`
3074
    /// let y = x.sqrt();
3075
    /// ```
3076
    #[inline]
3077
    pub fn sqrt(self) -> Self {
×
3078
        self.unary_op(UnaryOperator::Sqrt)
×
3079
    }
3080

3081
    /// Apply the "tan" operator to the current float scalar or vector
3082
    /// expression.
3083
    ///
3084
    /// This is a unary operator, which applies to float scalar or vector
3085
    /// operand expressions to produce a float scalar or vector. It applies
3086
    /// component-wise to vector operand expressions.
3087
    ///
3088
    /// # Example
3089
    ///
3090
    /// ```
3091
    /// # use bevy_hanabi::*;
3092
    /// # use bevy::math::Vec3;
3093
    /// # let mut w = ExprWriter::new();
3094
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3095
    /// let x = w.lit(Vec3::ONE);
3096
    ///
3097
    /// // Tan: `y = tan(x);`
3098
    /// let y = x.tan();
3099
    /// ```
3100
    #[inline]
3101
    pub fn tan(self) -> Self {
×
3102
        self.unary_op(UnaryOperator::Tan)
×
3103
    }
3104

3105
    /// Apply the "unpack4x8snorm" operator to the current `u32` scalar
3106
    /// expression.
3107
    ///
3108
    /// This is a unary operator, which applies to `u32` scalar operand
3109
    /// expressions to produce a 4-component floating point vector of signed
3110
    /// normalized components in `[-1:1]`.
3111
    ///
3112
    /// # Example
3113
    ///
3114
    /// ```
3115
    /// # use bevy_hanabi::*;
3116
    /// # use bevy::math::Vec3;
3117
    /// # let mut w = ExprWriter::new();
3118
    /// // A literal expression `y = 0x7F007FFFu32;`.
3119
    /// let y = w.lit(0x7F007FFFu32);
3120
    ///
3121
    /// // Unpack: `x = unpack4x8snorm(y);`
3122
    /// let x = y.unpack4x8snorm(); // vec4<f32>(-1., 1., 0., 7.2)
3123
    /// ```
3124
    #[inline]
3125
    pub fn unpack4x8snorm(self) -> Self {
×
3126
        self.unary_op(UnaryOperator::Unpack4x8snorm)
×
3127
    }
3128

3129
    /// Apply the "unpack4x8unorm" operator to the current `u32` scalar
3130
    /// expression.
3131
    ///
3132
    /// This is a unary operator, which applies to `u32` scalar operand
3133
    /// expressions to produce a 4-component floating point vector of unsigned
3134
    /// normalized components in `[0:1]`.
3135
    ///
3136
    /// # Example
3137
    ///
3138
    /// ```
3139
    /// # use bevy_hanabi::*;
3140
    /// # use bevy::math::Vec3;
3141
    /// # let mut w = ExprWriter::new();
3142
    /// // A literal expression `y = 0xFF00FF00u32;`.
3143
    /// let y = w.lit(0xFF00FF00u32);
3144
    ///
3145
    /// // Unpack: `x = unpack4x8unorm(y);`
3146
    /// let x = y.unpack4x8unorm(); // vec4<f32>(-1., 1., 0., 7.2)
3147
    /// ```
3148
    #[inline]
3149
    pub fn unpack4x8unorm(self) -> Self {
×
3150
        self.unary_op(UnaryOperator::Unpack4x8unorm)
×
3151
    }
3152

3153
    /// Apply the "saturate" operator to the current float scalar or vector
3154
    /// expression.
3155
    ///
3156
    /// This is a unary operator, which applies to float scalar or vector
3157
    /// operand expressions to produce a float scalar or vector. It applies
3158
    /// component-wise to vector operand expressions.
3159
    ///
3160
    /// # Example
3161
    ///
3162
    /// ```
3163
    /// # use bevy_hanabi::*;
3164
    /// # use bevy::math::Vec3;
3165
    /// # let mut w = ExprWriter::new();
3166
    /// // A literal expression `x = vec3<f32>(1., 1., 1.);`.
3167
    /// let x = w.lit(Vec3::ONE);
3168
    ///
3169
    /// // Saturate: `y = saturate(x);`
3170
    /// let y = x.saturate();
3171
    /// ```
3172
    #[inline]
3173
    pub fn saturate(self) -> Self {
×
3174
        self.unary_op(UnaryOperator::Saturate)
×
3175
    }
3176

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

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

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

3234
    /// Get the fourth component of a vector.
3235
    ///
3236
    /// # Example
3237
    ///
3238
    /// ```
3239
    /// # use bevy_hanabi::*;
3240
    /// # use bevy::math::Vec3;
3241
    /// # let mut w = ExprWriter::new();
3242
    /// // A literal expression `v = vec3<f32>(1., 1., 1.);`.
3243
    /// let v = w.lit(Vec3::ONE);
3244
    ///
3245
    /// // f = v.w;`
3246
    /// let f = v.w();
3247
    /// ```
3248
    #[inline]
3249
    pub fn w(self) -> Self {
×
3250
        self.unary_op(UnaryOperator::W)
×
3251
    }
3252

3253
    fn binary_op(self, other: Self, op: BinaryOperator) -> Self {
5✔
3254
        assert_eq!(self.module, other.module);
5✔
3255
        let left = self.expr;
10✔
3256
        let right = other.expr;
10✔
3257
        let expr = self
15✔
3258
            .module
10✔
3259
            .borrow_mut()
3260
            .push(Expr::Binary { op, left, right });
15✔
3261
        WriterExpr {
3262
            expr,
3263
            module: self.module,
5✔
3264
        }
3265
    }
3266

3267
    /// Add the current expression with another expression.
3268
    ///
3269
    /// This is a binary operator, which applies component-wise to vector
3270
    /// operand expressions.
3271
    ///
3272
    /// You can also use the [`std::ops::Add`] trait directly, via the `+`
3273
    /// symbol, as an alternative to calling this method directly.
3274
    ///
3275
    /// # Example
3276
    ///
3277
    /// ```
3278
    /// # use bevy_hanabi::*;
3279
    /// # use bevy::math::Vec2;
3280
    /// # let mut w = ExprWriter::new();
3281
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3282
    /// let x = w.lit(Vec2::new(3., -2.));
3283
    ///
3284
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3285
    /// let y = w.lit(Vec2::new(1., 5.));
3286
    ///
3287
    /// // The sum of both vectors `z = x + y;`.
3288
    /// let z = x.add(y); // == vec2<f32>(4., 3.)
3289
    ///                   // -OR-
3290
    ///                   // let z = x + y;
3291
    /// ```
3292
    #[allow(clippy::should_implement_trait)]
3293
    #[inline]
3294
    pub fn add(self, other: Self) -> Self {
2✔
3295
        self.binary_op(other, BinaryOperator::Add)
8✔
3296
    }
3297

3298
    /// Apply the "atan2" (inverse tangent with 2 arguments) operator to the
3299
    /// current float scalar or vector expression.
3300
    ///
3301
    /// This is a unary operator, which applies to float scalar or vector
3302
    /// operand expressions to produce a float scalar or vector. It applies
3303
    /// component-wise to vector operand expressions. The return value lies in
3304
    /// the -π ≤ x ≤ π range, and represents a value whose tangent is equal to
3305
    /// y over x (`z = atan2(y, x)` <=> `tan(z) = y / x`).
3306
    ///
3307
    /// # Example
3308
    ///
3309
    /// ```
3310
    /// # use bevy_hanabi::*;
3311
    /// # use bevy::math::Vec3;
3312
    /// # let mut w = ExprWriter::new();
3313
    /// // Two literal expressions `x` and `y`.
3314
    /// let x = w.lit(Vec3::new(1., 0., -1.));
3315
    /// let y = w.lit(Vec3::ONE);
3316
    ///
3317
    /// // Atan: `z = atan2(y, x);`
3318
    /// let z = y.atan2(x);
3319
    /// ```
3320
    #[inline]
3321
    pub fn atan2(self, other: Self) -> Self {
×
3322
        self.binary_op(other, BinaryOperator::Atan2)
×
3323
    }
3324

3325
    /// Calculate the cross product of the current expression by another
3326
    /// expression.
3327
    ///
3328
    /// This is a binary operator, which applies to vector operands of size 3
3329
    /// only, and always produces a vector of the same size.
3330
    ///
3331
    /// # Example
3332
    ///
3333
    /// ```
3334
    /// # use bevy_hanabi::*;
3335
    /// # use bevy::math::Vec3;
3336
    /// # let mut w = ExprWriter::new();
3337
    /// // A literal expression `x = vec3<f32>(3., -2., 1.);`.
3338
    /// let x = w.lit(Vec3::new(3., -2., 1.));
3339
    ///
3340
    /// // Another literal expression `y = vec3<f32>(1., 5., 0.);`.
3341
    /// let y = w.lit(Vec3::new(1., 5., 0.));
3342
    ///
3343
    /// // The cross product of both vectors `z = cross(x, y);`.
3344
    /// let z = x.cross(y);
3345
    /// ```
3346
    #[inline]
3347
    pub fn cross(self, other: Self) -> Self {
×
3348
        self.binary_op(other, BinaryOperator::Cross)
×
3349
    }
3350

3351
    /// Calculate the dot product of the current expression by another
3352
    /// expression.
3353
    ///
3354
    /// This is a binary operator, which applies to vector operands of same size
3355
    /// only, and always produces a floating point scalar.
3356
    ///
3357
    /// # Example
3358
    ///
3359
    /// ```
3360
    /// # use bevy_hanabi::*;
3361
    /// # use bevy::math::Vec2;
3362
    /// # let mut w = ExprWriter::new();
3363
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3364
    /// let x = w.lit(Vec2::new(3., -2.));
3365
    ///
3366
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3367
    /// let y = w.lit(Vec2::new(1., 5.));
3368
    ///
3369
    /// // The dot product of both vectors `z = dot(x, y);`.
3370
    /// let z = x.dot(y);
3371
    /// ```
3372
    #[inline]
3373
    pub fn dot(self, other: Self) -> Self {
×
3374
        self.binary_op(other, BinaryOperator::Dot)
×
3375
    }
3376

3377
    /// Calculate the distance between the current expression and another
3378
    /// expression.
3379
    ///
3380
    /// This is a binary operator.
3381
    ///
3382
    /// # Example
3383
    ///
3384
    /// ```
3385
    /// # use bevy_hanabi::*;
3386
    /// # use bevy::math::Vec3;
3387
    /// # let mut w = ExprWriter::new();
3388
    /// // A literal expression `x = vec3<f32>(3., -2., 1.);`.
3389
    /// let x = w.lit(Vec3::new(3., -2., 1.));
3390
    ///
3391
    /// // Another literal expression `y = vec3<f32>(1., 5., 0.);`.
3392
    /// let y = w.lit(Vec3::new(1., 5., 0.));
3393
    ///
3394
    /// // The distance between the vectors `z = distance(x, y);`.
3395
    /// let z = x.distance(y);
3396
    /// ```
3397
    #[inline]
3398
    pub fn distance(self, other: Self) -> Self {
×
3399
        self.binary_op(other, BinaryOperator::Distance)
×
3400
    }
3401

3402
    /// Divide the current expression by another expression.
3403
    ///
3404
    /// This is a binary operator, which applies component-wise to vector
3405
    /// operand expressions.
3406
    ///
3407
    /// You can also use the [`std::ops::Div`] trait directly, via the `/`
3408
    /// symbol, as an alternative to calling this method directly.
3409
    ///
3410
    /// # Example
3411
    ///
3412
    /// ```
3413
    /// # use bevy_hanabi::*;
3414
    /// # use bevy::math::Vec2;
3415
    /// # let mut w = ExprWriter::new();
3416
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3417
    /// let x = w.lit(Vec2::new(3., -2.));
3418
    ///
3419
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3420
    /// let y = w.lit(Vec2::new(1., 5.));
3421
    ///
3422
    /// // The quotient of both vectors `z = x / y;`.
3423
    /// let z = x.div(y); // == vec2<f32>(3., -0.4)
3424
    ///                   // -OR-
3425
    ///                   // let z = x / y;
3426
    /// ```
3427
    #[allow(clippy::should_implement_trait)]
3428
    #[inline]
3429
    pub fn div(self, other: Self) -> Self {
×
3430
        self.binary_op(other, BinaryOperator::Div)
×
3431
    }
3432

3433
    /// Apply the logical operator "greater than or equal" to this expression
3434
    /// and another expression.
3435
    ///
3436
    /// This is a binary operator, which applies component-wise to vector
3437
    /// operand expressions.
3438
    ///
3439
    /// # Example
3440
    ///
3441
    /// ```
3442
    /// # use bevy_hanabi::*;
3443
    /// # use bevy::math::Vec3;
3444
    /// # let mut w = ExprWriter::new();
3445
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3446
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3447
    ///
3448
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3449
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3450
    ///
3451
    /// // The boolean result of the greater than or equal operation `z = (x >= y);`.
3452
    /// let z = x.ge(y); // == vec3<bool>(true, false, true)
3453
    /// ```
3454
    #[inline]
3455
    pub fn ge(self, other: Self) -> Self {
×
3456
        self.binary_op(other, BinaryOperator::GreaterThanOrEqual)
×
3457
    }
3458

3459
    /// Apply the logical operator "greater than" to this expression and another
3460
    /// expression.
3461
    ///
3462
    /// This is a binary operator, which applies component-wise to vector
3463
    /// operand expressions.
3464
    ///
3465
    /// # Example
3466
    ///
3467
    /// ```
3468
    /// # use bevy_hanabi::*;
3469
    /// # use bevy::math::Vec3;
3470
    /// # let mut w = ExprWriter::new();
3471
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3472
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3473
    ///
3474
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3475
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3476
    ///
3477
    /// // The boolean result of the greater than operation `z = (x > y);`.
3478
    /// let z = x.gt(y); // == vec3<bool>(true, false, false)
3479
    /// ```
3480
    #[inline]
3481
    pub fn gt(self, other: Self) -> Self {
×
3482
        self.binary_op(other, BinaryOperator::GreaterThan)
×
3483
    }
3484

3485
    /// Apply the logical operator "less than or equal" to this expression and
3486
    /// another expression.
3487
    ///
3488
    /// This is a binary operator, which applies component-wise to vector
3489
    /// operand expressions.
3490
    ///
3491
    /// # Example
3492
    ///
3493
    /// ```
3494
    /// # use bevy_hanabi::*;
3495
    /// # use bevy::math::Vec3;
3496
    /// # let mut w = ExprWriter::new();
3497
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3498
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3499
    ///
3500
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3501
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3502
    ///
3503
    /// // The boolean result of the less than or equal operation `z = (x <= y);`.
3504
    /// let z = x.le(y); // == vec3<bool>(false, true, true)
3505
    /// ```
3506
    #[inline]
3507
    pub fn le(self, other: Self) -> Self {
×
3508
        self.binary_op(other, BinaryOperator::LessThanOrEqual)
×
3509
    }
3510

3511
    /// Apply the logical operator "less than" to this expression and another
3512
    /// expression.
3513
    ///
3514
    /// This is a binary operator, which applies component-wise to vector
3515
    /// operand expressions.
3516
    ///
3517
    /// # Example
3518
    ///
3519
    /// ```
3520
    /// # use bevy_hanabi::*;
3521
    /// # use bevy::math::Vec3;
3522
    /// # let mut w = ExprWriter::new();
3523
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3524
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3525
    ///
3526
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3527
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3528
    ///
3529
    /// // The boolean result of the less than operation `z = (x < y);`.
3530
    /// let z = x.lt(y); // == vec3<bool>(false, true, false)
3531
    /// ```
3532
    #[inline]
3533
    pub fn lt(self, other: Self) -> Self {
×
3534
        self.binary_op(other, BinaryOperator::LessThan)
×
3535
    }
3536

3537
    /// Take the maximum value of the current expression and another expression.
3538
    ///
3539
    /// This is a binary operator, which applies component-wise to vector
3540
    /// operand expressions.
3541
    ///
3542
    /// # Example
3543
    ///
3544
    /// ```
3545
    /// # use bevy_hanabi::*;
3546
    /// # use bevy::math::Vec2;
3547
    /// # let mut w = ExprWriter::new();
3548
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3549
    /// let x = w.lit(Vec2::new(3., -2.));
3550
    ///
3551
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3552
    /// let y = w.lit(Vec2::new(1., 5.));
3553
    ///
3554
    /// // The maximum of both vectors `z = max(x, y);`.
3555
    /// let z = x.max(y); // == vec2<f32>(3., 5.)
3556
    /// ```
3557
    #[inline]
3558
    pub fn max(self, other: Self) -> Self {
1✔
3559
        self.binary_op(other, BinaryOperator::Max)
4✔
3560
    }
3561

3562
    /// Take the minimum value of the current expression and another expression.
3563
    ///
3564
    /// This is a binary operator, which applies component-wise to vector
3565
    /// operand expressions.
3566
    ///
3567
    /// # Example
3568
    ///
3569
    /// ```
3570
    /// # use bevy_hanabi::*;
3571
    /// # use bevy::math::Vec2;
3572
    /// # let mut w = ExprWriter::new();
3573
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3574
    /// let x = w.lit(Vec2::new(3., -2.));
3575
    ///
3576
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3577
    /// let y = w.lit(Vec2::new(1., 5.));
3578
    ///
3579
    /// // The minimum of both vectors `z = min(x, y);`.
3580
    /// let z = x.min(y); // == vec2<f32>(1., -2.)
3581
    /// ```
3582
    #[inline]
3583
    pub fn min(self, other: Self) -> Self {
1✔
3584
        self.binary_op(other, BinaryOperator::Min)
4✔
3585
    }
3586

3587
    /// Multiply the current expression with another expression.
3588
    ///
3589
    /// This is a binary operator, which applies component-wise to vector
3590
    /// operand expressions.
3591
    ///
3592
    /// You can also use the [`std::ops::Mul`] trait directly, via the `*`
3593
    /// symbol, as an alternative to calling this method directly.
3594
    ///
3595
    /// # Example
3596
    ///
3597
    /// ```
3598
    /// # use bevy_hanabi::*;
3599
    /// # use bevy::math::Vec2;
3600
    /// # let mut w = ExprWriter::new();
3601
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3602
    /// let x = w.lit(Vec2::new(3., -2.));
3603
    ///
3604
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3605
    /// let y = w.lit(Vec2::new(1., 5.));
3606
    ///
3607
    /// // The product of both vectors `z = x * y;`.
3608
    /// let z = x.mul(y); // == vec2<f32>(3., -10.)
3609
    ///                   // -OR-
3610
    ///                   // let z = x * y;
3611
    /// ```
3612
    #[allow(clippy::should_implement_trait)]
3613
    #[inline]
3614
    pub fn mul(self, other: Self) -> Self {
1✔
3615
        self.binary_op(other, BinaryOperator::Mul)
4✔
3616
    }
3617

3618
    /// Apply the logical operator "normal" to this expression and another
3619
    /// expression.
3620
    ///
3621
    /// This is a binary operator, which applies component-wise to vector
3622
    /// operand expressions. That is, for vectors, this produces a vector of
3623
    /// random values where each component is normally distributed with a mean
3624
    /// of the corresponding component of the first operand and a standard
3625
    /// deviation of the corresponding component of the second operand.
3626
    ///
3627
    /// # Example
3628
    ///
3629
    /// ```
3630
    /// # use bevy_hanabi::*;
3631
    /// # use bevy::math::Vec3;
3632
    /// # let mut w = ExprWriter::new();
3633
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3634
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3635
    ///
3636
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3637
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3638
    ///
3639
    /// // A random variable normally distributed in [1:3]x[-2:5]x[7:7].
3640
    /// let z = x.normal(y);
3641
    /// ```
3642
    #[inline]
NEW
3643
    pub fn normal(self, other: Self) -> Self {
×
NEW
3644
        self.binary_op(other, BinaryOperator::NormalRand)
×
3645
    }
3646

3647
    /// Calculate the remainder of the division of the current expression by
3648
    /// another expression.
3649
    ///
3650
    /// This is a binary operator.
3651
    ///
3652
    /// # Example
3653
    ///
3654
    /// ```
3655
    /// # use bevy_hanabi::*;
3656
    /// # use bevy::math::Vec3;
3657
    /// # let mut w = ExprWriter::new();
3658
    /// // A literal expression `x = vec3<f32>(3., -2., 1.);`.
3659
    /// let x = w.lit(Vec3::new(3., -2., 1.));
3660
    ///
3661
    /// // Another literal expression `y = vec3<f32>(1., 5., 0.);`.
3662
    /// let y = w.lit(Vec3::new(1., 5., 0.));
3663
    ///
3664
    /// // The remainder of the division `z = x % y;`.
3665
    /// let z = x.rem(y);
3666
    /// ```
3667
    #[allow(clippy::should_implement_trait)]
3668
    #[inline]
3669
    pub fn rem(self, other: Self) -> Self {
×
3670
        self.binary_op(other, BinaryOperator::Remainder)
×
3671
    }
3672

3673
    /// Calculate the step of a value with respect to a reference.
3674
    ///
3675
    /// This is a binary operator, which applies component-wise to vector
3676
    /// operand expressions.
3677
    ///
3678
    /// # Example
3679
    ///
3680
    /// ```
3681
    /// # use bevy_hanabi::*;
3682
    /// # use bevy::math::Vec3;
3683
    /// # let mut w = ExprWriter::new();
3684
    /// // A literal expression `x = vec3<f32>(3., -2.);`.
3685
    /// let x = w.lit(Vec3::new(3., -2., 8.));
3686
    ///
3687
    /// // An edge reference `e = vec3<f32>(1., 5.);`.
3688
    /// let e = w.lit(Vec3::new(1., 5., 8.));
3689
    ///
3690
    /// // The step value
3691
    /// let s = x.step(e); // == vec3<f32>(1., 0., 1.)
3692
    /// ```
3693
    #[allow(clippy::should_implement_trait)]
3694
    #[inline]
3695
    pub fn step(self, edge: Self) -> Self {
×
3696
        // Note: order is step(edge, x) but x.step(edge)
3697
        edge.binary_op(self, BinaryOperator::Step)
×
3698
    }
3699

3700
    /// Subtract another expression from the current expression.
3701
    ///
3702
    /// This is a binary operator, which applies component-wise to vector
3703
    /// operand expressions.
3704
    ///
3705
    /// You can also use the [`std::ops::Sub`] trait directly, via the `-`
3706
    /// symbol, as an alternative to calling this method directly.
3707
    ///
3708
    /// # Example
3709
    ///
3710
    /// ```
3711
    /// # use bevy_hanabi::*;
3712
    /// # use bevy::math::Vec2;
3713
    /// # let mut w = ExprWriter::new();
3714
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3715
    /// let x = w.lit(Vec2::new(3., -2.));
3716
    ///
3717
    /// // Another literal expression `y = vec2<f32>(1., 5.);`.
3718
    /// let y = w.lit(Vec2::new(1., 5.));
3719
    ///
3720
    /// // The difference of both vectors `z = x - y;`.
3721
    /// let z = x.sub(y); // == vec2<f32>(2., -7.)
3722
    ///                   // -OR-
3723
    ///                   // let z = x - y;
3724
    /// ```
3725
    #[allow(clippy::should_implement_trait)]
3726
    #[inline]
3727
    pub fn sub(self, other: Self) -> Self {
×
3728
        self.binary_op(other, BinaryOperator::Sub)
×
3729
    }
3730

3731
    /// Apply the logical operator "uniform" to this expression and another
3732
    /// expression.
3733
    ///
3734
    /// This is a binary operator, which applies component-wise to vector
3735
    /// operand expressions. That is, for vectors, this produces a vector of
3736
    /// random values where each component is uniformly distributed within the
3737
    /// bounds of the related component of both operands.
3738
    ///
3739
    /// # Example
3740
    ///
3741
    /// ```
3742
    /// # use bevy_hanabi::*;
3743
    /// # use bevy::math::Vec3;
3744
    /// # let mut w = ExprWriter::new();
3745
    /// // A literal expression `x = vec3<f32>(3., -2., 7.);`.
3746
    /// let x = w.lit(Vec3::new(3., -2., 7.));
3747
    ///
3748
    /// // Another literal expression `y = vec3<f32>(1., 5., 7.);`.
3749
    /// let y = w.lit(Vec3::new(1., 5., 7.));
3750
    ///
3751
    /// // A random variable uniformly distributed in [1:3]x[-2:5]x[7:7].
3752
    /// let z = x.uniform(y);
3753
    /// ```
3754
    #[inline]
3755
    pub fn uniform(self, other: Self) -> Self {
×
3756
        self.binary_op(other, BinaryOperator::UniformRand)
×
3757
    }
3758

UNCOV
3759
    fn ternary_op(self, second: Self, third: Self, op: TernaryOperator) -> Self {
×
UNCOV
3760
        assert_eq!(self.module, second.module);
×
3761
        assert_eq!(self.module, third.module);
×
3762
        let first = self.expr;
×
3763
        let second = second.expr;
×
3764
        let third = third.expr;
×
3765
        let expr = self.module.borrow_mut().push(Expr::Ternary {
×
3766
            op,
×
3767
            first,
×
3768
            second,
×
3769
            third,
×
3770
        });
3771
        WriterExpr {
3772
            expr,
3773
            module: self.module,
×
3774
        }
3775
    }
3776

3777
    /// Blending linearly ("mix") two expressions with the fraction provided by
3778
    /// a third expression.
3779
    ///
3780
    /// This is a ternary operator, which applies component-wise to vector
3781
    /// operand expressions.
3782
    ///
3783
    /// # Example
3784
    ///
3785
    /// ```
3786
    /// # use bevy_hanabi::*;
3787
    /// # use bevy::math::Vec2;
3788
    /// # let mut w = ExprWriter::new();
3789
    /// // A literal expression `x = vec2<f32>(3., -2.);`.
3790
    /// let x = w.lit(Vec2::new(3., -2.));
3791
    ///
3792
    /// // Another literal expression `y = vec2<f32>(1., 4.);`.
3793
    /// let y = w.lit(Vec2::new(1., 4.));
3794
    ///
3795
    /// // A fraction `t = 0.5;`.
3796
    /// let t = w.lit(0.25);
3797
    ///
3798
    /// // The linear blend of x and y via t: z = (1 - t) * x + y * t
3799
    /// let z = x.mix(y, t); // == vec2<f32>(2.5, -0.5)
3800
    /// ```
3801
    #[inline]
3802
    pub fn mix(self, other: Self, fraction: Self) -> Self {
×
3803
        self.ternary_op(other, fraction, TernaryOperator::Mix)
×
3804
    }
3805

3806
    /// Clamp an expression between the bounds defined by two other expressions.
3807
    ///
3808
    /// This is a ternary operator, which applies component-wise to vector
3809
    /// operand expressions.
3810
    ///
3811
    /// # Example
3812
    ///
3813
    /// ```
3814
    /// # use bevy_hanabi::*;
3815
    /// # use bevy::math::Vec2;
3816
    /// # let mut w = ExprWriter::new();
3817
    /// // A literal expression `x = vec2<f32>(1., -2.);`.
3818
    /// let x = w.lit(Vec2::new(1., -2.));
3819
    ///
3820
    /// // A literal expression `lo = vec2<f32>(3., -3.);`.
3821
    /// let lo = w.lit(Vec2::new(3., -3.));
3822
    ///
3823
    /// // A literal expression `hi = vec2<f32>(5., 1.);`.
3824
    /// let hi = w.lit(Vec2::new(5., 1.));
3825
    ///
3826
    /// // Clamp x in [lo:hi]
3827
    /// let z = x.clamp(lo, hi); // == vec2<f32>(3., -2.)
3828
    /// ```
3829
    #[inline]
NEW
3830
    pub fn clamp(self, lo: Self, hi: Self) -> Self {
×
NEW
3831
        self.ternary_op(lo, hi, TernaryOperator::Clamp)
×
3832
    }
3833

3834
    /// Calculate the smooth Hermite interpolation in \[0:1\] of the current
3835
    /// value taken between the given bounds.
3836
    ///
3837
    /// This is a ternary operator, which applies component-wise to vector
3838
    /// operand expressions.
3839
    ///
3840
    /// # Example
3841
    ///
3842
    /// ```
3843
    /// # use bevy_hanabi::*;
3844
    /// # use bevy::math::Vec2;
3845
    /// # let mut w = ExprWriter::new();
3846
    /// // A literal expression `low = vec2<f32>(3., -2.);`.
3847
    /// let low = w.lit(Vec2::new(3., -2.));
3848
    ///
3849
    /// // Another literal expression `high = vec2<f32>(1., 4.);`.
3850
    /// let high = w.lit(Vec2::new(1., 4.));
3851
    ///
3852
    /// // A point `x = vec2<f32>(2., 1.);` between `low` and `high`.
3853
    /// let x = w.lit(Vec2::new(2., 1.));
3854
    ///
3855
    /// // The smooth Hermite interpolation: `t = smoothstep(low, high, x)`
3856
    /// let t = x.smoothstep(low, high); // == 0.5
3857
    /// ```
3858
    #[inline]
3859
    pub fn smoothstep(self, low: Self, high: Self) -> Self {
×
3860
        // Note: order is smoothstep(low, high, x) but x.smoothstep(low, high)
3861
        low.ternary_op(high, self, TernaryOperator::SmoothStep)
×
3862
    }
3863

3864
    /// Construct a `Vec2` from two scalars.
3865
    ///
3866
    /// # Example
3867
    ///
3868
    /// ```
3869
    /// # use bevy_hanabi::*;
3870
    /// # let mut w = ExprWriter::new();
3871
    /// let theta = w.add_property("theta", 0.0.into());
3872
    /// // Convert the angular property `theta` to a 2D vector.
3873
    /// let (cos_theta, sin_theta) = (w.prop(theta).cos(), w.prop(theta).sin());
3874
    /// let circle_pos = cos_theta.vec2(sin_theta);
3875
    /// ```
3876
    #[inline]
3877
    pub fn vec2(self, y: Self) -> Self {
×
3878
        self.binary_op(y, BinaryOperator::Vec2)
×
3879
    }
3880

3881
    /// Construct a `Vec3` from two scalars.
3882
    ///
3883
    /// # Example
3884
    ///
3885
    /// ```
3886
    /// # use bevy_hanabi::*;
3887
    /// # let mut w = ExprWriter::new();
3888
    /// let theta = w.add_property("theta", 0.0.into());
3889
    /// // Convert the angular property `theta` to a 3D vector in a flat plane.
3890
    /// let (cos_theta, sin_theta) = (w.prop(theta).cos(), w.prop(theta).sin());
3891
    /// let circle_pos = cos_theta.vec3(w.lit(0.0), sin_theta);
3892
    /// ```
3893
    #[inline]
3894
    pub fn vec3(self, y: Self, z: Self) -> Self {
×
3895
        self.ternary_op(y, z, TernaryOperator::Vec3)
×
3896
    }
3897

3898
    /// Construct a `Vec4` from a vector XYZ and a scalar W.
3899
    ///
3900
    /// # Example
3901
    ///
3902
    /// ```
3903
    /// # use bevy_hanabi::*;
3904
    /// # let mut w = ExprWriter::new();
3905
    /// let rgb = w.rand(VectorType::VEC3F);
3906
    /// let a = w.lit(1.);
3907
    /// // Build vec4<f32>(R, G, B, A) and convert to 0xAABBGGRR
3908
    /// let col = rgb.vec4_xyz_w(a).pack4x8unorm();
3909
    /// ```
3910
    #[inline]
3911
    pub fn vec4_xyz_w(self, w: Self) -> Self {
×
3912
        self.binary_op(w, BinaryOperator::Vec4XyzW)
×
3913
    }
3914

3915
    /// Cast an expression to a different type.
3916
    ///
3917
    /// # Example
3918
    ///
3919
    /// ```
3920
    /// # use bevy_hanabi::*;
3921
    /// # use bevy::math::Vec2;
3922
    /// # let mut w = ExprWriter::new();
3923
    /// let x = w.lit(Vec2::new(3., -2.));
3924
    /// let y = x.cast(VectorType::VEC3I); // x = vec3<i32>(particle.position);
3925
    /// ```
3926
    pub fn cast(self, target: impl Into<ValueType>) -> Self {
×
3927
        let target = target.into();
×
3928
        let expr = self
×
3929
            .module
×
3930
            .borrow_mut()
3931
            .push(Expr::Cast(CastExpr::new(self.expr, target)));
×
3932
        WriterExpr {
3933
            expr,
3934
            module: self.module,
×
3935
        }
3936
    }
3937

3938
    /// Finalize an expression chain and return the accumulated expression.
3939
    ///
3940
    /// The returned handle indexes the [`Module`] owned by the [`ExprWriter`]
3941
    /// this intermediate expression was built from.
3942
    ///
3943
    /// # Example
3944
    ///
3945
    /// ```
3946
    /// # use bevy_hanabi::*;
3947
    /// # let mut w = ExprWriter::new();
3948
    /// // A literal expression `x = -3.5;`.
3949
    /// let x = w.lit(-3.5);
3950
    ///
3951
    /// // Retrieve the ExprHandle for that expression.
3952
    /// let handle = x.expr();
3953
    /// ```
3954
    #[inline]
3955
    pub fn expr(self) -> ExprHandle {
8✔
3956
        self.expr
8✔
3957
    }
3958
}
3959

3960
impl std::ops::Add<WriterExpr> for WriterExpr {
3961
    type Output = WriterExpr;
3962

3963
    #[inline]
3964
    fn add(self, rhs: WriterExpr) -> Self::Output {
2✔
3965
        self.add(rhs)
6✔
3966
    }
3967
}
3968

3969
impl std::ops::Sub<WriterExpr> for WriterExpr {
3970
    type Output = WriterExpr;
3971

3972
    #[inline]
3973
    fn sub(self, rhs: WriterExpr) -> Self::Output {
×
3974
        self.sub(rhs)
×
3975
    }
3976
}
3977

3978
impl std::ops::Mul<WriterExpr> for WriterExpr {
3979
    type Output = WriterExpr;
3980

3981
    #[inline]
3982
    fn mul(self, rhs: WriterExpr) -> Self::Output {
1✔
3983
        self.mul(rhs)
3✔
3984
    }
3985
}
3986

3987
impl std::ops::Div<WriterExpr> for WriterExpr {
3988
    type Output = WriterExpr;
3989

3990
    #[inline]
3991
    fn div(self, rhs: WriterExpr) -> Self::Output {
×
3992
        self.div(rhs)
×
3993
    }
3994
}
3995

3996
impl std::ops::Rem<WriterExpr> for WriterExpr {
3997
    type Output = WriterExpr;
3998

3999
    #[inline]
4000
    fn rem(self, rhs: WriterExpr) -> Self::Output {
×
4001
        self.rem(rhs)
×
4002
    }
4003
}
4004

4005
#[cfg(test)]
4006
mod tests {
4007
    use bevy::{platform::collections::HashSet, prelude::*};
4008

4009
    use super::*;
4010
    use crate::{MatrixType, ScalarValue, ShaderWriter, VectorType};
4011

4012
    #[test]
4013
    fn module() {
4014
        let mut m = Module::default();
4015

4016
        #[allow(unsafe_code)]
4017
        let unknown = unsafe { ExprHandle::new_unchecked(1) };
4018
        assert!(m.get(unknown).is_none());
4019
        assert!(m.get_mut(unknown).is_none());
4020
        assert!(matches!(
4021
            m.try_get(unknown),
4022
            Err(ExprError::InvalidExprHandleError(_))
4023
        ));
4024
        assert!(matches!(
4025
            m.try_get_mut(unknown),
4026
            Err(ExprError::InvalidExprHandleError(_))
4027
        ));
4028

4029
        let x = m.lit(5.);
4030
        let mut expected = Expr::Literal(LiteralExpr::new(5.));
4031
        assert_eq!(m.get(x), Some(&expected));
4032
        assert_eq!(m.get_mut(x), Some(&mut expected));
4033
        assert_eq!(m.try_get(x), Ok(&expected));
4034
        assert_eq!(m.try_get_mut(x), Ok(&mut expected));
4035
    }
4036

4037
    #[test]
4038
    fn local_var() {
4039
        let property_layout = PropertyLayout::default();
4040
        let particle_layout = ParticleLayout::default();
4041
        let mut ctx =
4042
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4043
        let mut h = HashSet::new();
4044
        for _ in 0..100 {
4045
            let v = ctx.make_local_var();
4046
            assert!(h.insert(v));
4047
        }
4048
    }
4049

4050
    #[test]
4051
    fn make_fn() {
4052
        let property_layout = PropertyLayout::default();
4053
        let particle_layout = ParticleLayout::default();
4054
        let mut ctx =
4055
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4056
        let mut module = Module::default();
4057

4058
        // Make a function
4059
        let func_name = "my_func";
4060
        let args = "arg0: i32, arg1: f32";
4061
        assert!(ctx
4062
            .make_fn(func_name, args, &mut module, &mut |m, ctx| {
4063
                m.lit(3.);
4064
                let v = ctx.make_local_var();
4065
                assert_eq!(v, "var0");
4066
                let code = String::new();
4067
                Ok(code)
4068
            })
4069
            .is_ok());
4070

4071
        // The local function context doesn't influence the outer caller context
4072
        let v = ctx.make_local_var();
4073
        assert_eq!(v, "var0");
4074

4075
        // However the module is common to the outer caller and the function
4076
        assert!(!module.expressions.is_empty());
4077
    }
4078

4079
    #[test]
4080
    fn property() {
4081
        let mut m = Module::default();
4082

4083
        let _my_prop = m.add_property("my_prop", Value::Scalar(345_u32.into()));
4084
        let _other_prop = m.add_property(
4085
            "other_prop",
4086
            Value::Vector(Vec3::new(3., -7.5, 42.42).into()),
4087
        );
4088

4089
        assert!(m.properties().iter().any(|p| p.name() == "my_prop"));
4090
        assert!(m.properties().iter().any(|p| p.name() == "other_prop"));
4091
        assert!(!m.properties().iter().any(|p| p.name() == "do_not_exist"));
4092
    }
4093

4094
    #[test]
4095
    fn writer() {
4096
        // Get a module and its writer
4097
        let w = ExprWriter::new();
4098
        let my_prop = w.add_property("my_prop", 3.0.into());
4099

4100
        // Build some expression
4101
        let x = w.lit(3.).abs().max(w.attr(Attribute::POSITION) * w.lit(2.))
4102
            + w.lit(-4.).min(w.prop(my_prop));
4103
        let x = x.expr();
4104

4105
        // Create an evaluation context
4106
        let property_layout =
4107
            PropertyLayout::new(&[Property::new("my_prop", ScalarValue::Float(3.))]);
4108
        let particle_layout = ParticleLayout::default();
4109
        let m = w.finish();
4110
        let mut context =
4111
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4112

4113
        // Evaluate the expression
4114
        let x = m.try_get(x).unwrap();
4115
        let s = x.eval(&m, &mut context).unwrap();
4116
        assert_eq!(
4117
            "(max(abs(3.), (particle.position) * (2.))) + (min(-4., properties.my_prop))"
4118
                .to_string(),
4119
            s
4120
        );
4121
    }
4122

4123
    #[test]
4124
    fn type_error() {
4125
        let l = Value::Scalar(3.5_f32.into());
4126
        let r: Result<Vec2, ExprError> = l.try_into();
4127
        assert!(r.is_err());
4128
        assert!(matches!(r, Err(ExprError::TypeError(_))));
4129
    }
4130

4131
    #[test]
4132
    fn math_expr() {
4133
        let mut m = Module::default();
4134

4135
        let x = m.attr(Attribute::POSITION);
4136
        let y = m.lit(Vec3::ONE);
4137

4138
        let add = m.add(x, y);
4139
        let sub = m.sub(x, y);
4140
        let mul = m.mul(x, y);
4141
        let div = m.div(x, y);
4142
        let rem = m.rem(x, y);
4143
        let lt = m.lt(x, y);
4144
        let le = m.le(x, y);
4145
        let gt = m.gt(x, y);
4146
        let ge = m.ge(x, y);
4147

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

4153
        for (expr, op) in [
4154
            (add, "+"),
4155
            (sub, "-"),
4156
            (mul, "*"),
4157
            (div, "/"),
4158
            (rem, "%"),
4159
            (lt, "<"),
4160
            (le, "<="),
4161
            (gt, ">"),
4162
            (ge, ">="),
4163
        ] {
4164
            let expr = ctx.eval(&m, expr);
4165
            assert!(expr.is_ok());
4166
            let expr = expr.unwrap();
4167
            assert_eq!(
4168
                expr,
4169
                format!(
4170
                    "(particle.{}) {} (vec3<f32>(1.,1.,1.))",
4171
                    Attribute::POSITION.name(),
4172
                    op,
4173
                )
4174
            );
4175
        }
4176
    }
4177

4178
    #[test]
4179
    fn builtin_expr() {
4180
        let mut m = Module::default();
4181

4182
        // Simulation parameters
4183
        for op in [
4184
            BuiltInOperator::Time,
4185
            BuiltInOperator::DeltaTime,
4186
            BuiltInOperator::VirtualTime,
4187
            BuiltInOperator::VirtualDeltaTime,
4188
            BuiltInOperator::RealTime,
4189
            BuiltInOperator::RealDeltaTime,
4190
        ] {
4191
            let value = m.builtin(op);
4192

4193
            let property_layout = PropertyLayout::default();
4194
            let particle_layout = ParticleLayout::default();
4195
            let mut ctx =
4196
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4197

4198
            let expr = ctx.eval(&m, value);
4199
            assert!(expr.is_ok());
4200
            let expr = expr.unwrap();
4201
            assert_eq!(expr, format!("sim_params.{}", op.name()));
4202
        }
4203

4204
        // is_alive
4205
        {
4206
            let value = m.builtin(BuiltInOperator::IsAlive);
4207

4208
            let property_layout = PropertyLayout::default();
4209
            let particle_layout = ParticleLayout::default();
4210
            let mut ctx =
4211
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4212

4213
            let expr = ctx.eval(&m, value);
4214
            assert!(expr.is_ok());
4215
            let expr = expr.unwrap();
4216
            assert_eq!(expr, "is_alive");
4217
        }
4218

4219
        // BuiltInOperator::Rand (which has side effect)
4220
        for (scalar_type, prefix) in [
4221
            (ScalarType::Bool, "b"),
4222
            (ScalarType::Float, "f"),
4223
            (ScalarType::Int, "i"),
4224
            (ScalarType::Uint, "u"),
4225
        ] {
4226
            let value = m.builtin(BuiltInOperator::Rand(scalar_type.into()));
4227

4228
            // Scalar form
4229
            {
4230
                let property_layout = PropertyLayout::default();
4231
                let particle_layout = ParticleLayout::default();
4232
                let mut ctx =
4233
                    ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4234

4235
                let expr = ctx.eval(&m, value);
4236
                assert!(expr.is_ok());
4237
                let expr = expr.unwrap();
4238
                assert_eq!(expr, "var0");
4239
                assert_eq!(ctx.main_code, format!("let var0 = {}rand();\n", prefix));
4240
            }
4241

4242
            // Vector form
4243
            for count in 2..=4 {
4244
                let vec = m.builtin(BuiltInOperator::Rand(
4245
                    VectorType::new(scalar_type, count).into(),
4246
                ));
4247

4248
                let property_layout = PropertyLayout::default();
4249
                let particle_layout = ParticleLayout::default();
4250
                let mut ctx =
4251
                    ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4252

4253
                let expr = ctx.eval(&m, vec);
4254
                assert!(expr.is_ok());
4255
                let expr = expr.unwrap();
4256
                assert_eq!(expr, "var0");
4257
                assert_eq!(
4258
                    ctx.main_code,
4259
                    format!("let var0 = {}rand{}();\n", prefix, count)
4260
                );
4261
            }
4262
        }
4263
    }
4264

4265
    #[test]
4266
    fn unary_expr() {
4267
        let mut m = Module::default();
4268

4269
        let x = m.attr(Attribute::POSITION);
4270
        let y = m.lit(Vec3::new(1., -3.1, 6.99));
4271
        let z = m.lit(BVec3::new(false, true, false));
4272
        let w = m.lit(Vec4::W);
4273
        let v = m.lit(Vec4::new(-1., 1., 0., 7.2));
4274
        let us = m.lit(0x0u32);
4275
        let uu = m.lit(0x0u32);
4276

4277
        let abs = m.abs(x);
4278
        let acos = m.acos(w);
4279
        let all = m.all(z);
4280
        let any = m.any(z);
4281
        let asin = m.asin(w);
4282
        let atan = m.atan(w);
4283
        let ceil = m.ceil(y);
4284
        let cos = m.cos(y);
4285
        let exp = m.exp(y);
4286
        let exp2 = m.exp2(y);
4287
        let floor = m.floor(y);
4288
        let fract = m.fract(y);
4289
        let inv_sqrt = m.inverse_sqrt(y);
4290
        let length = m.length(y);
4291
        let log = m.log(y);
4292
        let log2 = m.log2(y);
4293
        let norm = m.normalize(y);
4294
        let pack4x8snorm = m.pack4x8snorm(v);
4295
        let pack4x8unorm = m.pack4x8unorm(v);
4296
        let round = m.round(y);
4297
        let saturate = m.saturate(y);
4298
        let sign = m.sign(y);
4299
        let sin = m.sin(y);
4300
        let sqrt = m.sqrt(y);
4301
        let tan = m.tan(y);
4302
        let unpack4x8snorm = m.unpack4x8snorm(us);
4303
        let unpack4x8unorm = m.unpack4x8unorm(uu);
4304
        let comp_x = m.x(w);
4305
        let comp_y = m.y(w);
4306
        let comp_z = m.z(w);
4307
        let comp_w = m.w(w);
4308

4309
        let property_layout = PropertyLayout::default();
4310
        let particle_layout = ParticleLayout::default();
4311
        let mut ctx =
4312
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4313

4314
        for (expr, op, inner) in [
4315
            (
4316
                abs,
4317
                "abs",
4318
                &format!("particle.{}", Attribute::POSITION.name())[..],
4319
            ),
4320
            (acos, "acos", "vec4<f32>(0.,0.,0.,1.)"),
4321
            (all, "all", "vec3<bool>(false,true,false)"),
4322
            (any, "any", "vec3<bool>(false,true,false)"),
4323
            (asin, "asin", "vec4<f32>(0.,0.,0.,1.)"),
4324
            (atan, "atan", "vec4<f32>(0.,0.,0.,1.)"),
4325
            (ceil, "ceil", "vec3<f32>(1.,-3.1,6.99)"),
4326
            (cos, "cos", "vec3<f32>(1.,-3.1,6.99)"),
4327
            (exp, "exp", "vec3<f32>(1.,-3.1,6.99)"),
4328
            (exp2, "exp2", "vec3<f32>(1.,-3.1,6.99)"),
4329
            (floor, "floor", "vec3<f32>(1.,-3.1,6.99)"),
4330
            (fract, "fract", "vec3<f32>(1.,-3.1,6.99)"),
4331
            (inv_sqrt, "inverseSqrt", "vec3<f32>(1.,-3.1,6.99)"),
4332
            (length, "length", "vec3<f32>(1.,-3.1,6.99)"),
4333
            (log, "log", "vec3<f32>(1.,-3.1,6.99)"),
4334
            (log2, "log2", "vec3<f32>(1.,-3.1,6.99)"),
4335
            (norm, "normalize", "vec3<f32>(1.,-3.1,6.99)"),
4336
            (pack4x8snorm, "pack4x8snorm", "vec4<f32>(-1.,1.,0.,7.2)"),
4337
            (pack4x8unorm, "pack4x8unorm", "vec4<f32>(-1.,1.,0.,7.2)"),
4338
            (round, "round", "vec3<f32>(1.,-3.1,6.99)"),
4339
            (saturate, "saturate", "vec3<f32>(1.,-3.1,6.99)"),
4340
            (sign, "sign", "vec3<f32>(1.,-3.1,6.99)"),
4341
            (sin, "sin", "vec3<f32>(1.,-3.1,6.99)"),
4342
            (sqrt, "sqrt", "vec3<f32>(1.,-3.1,6.99)"),
4343
            (tan, "tan", "vec3<f32>(1.,-3.1,6.99)"),
4344
            (unpack4x8snorm, "unpack4x8snorm", "0u"),
4345
            (unpack4x8unorm, "unpack4x8unorm", "0u"),
4346
        ] {
4347
            let expr = ctx.eval(&m, expr);
4348
            assert!(expr.is_ok());
4349
            let expr = expr.unwrap();
4350
            assert_eq!(expr, format!("{}({})", op, inner));
4351
        }
4352

4353
        for (expr, op, inner) in [
4354
            (comp_x, "x", "vec4<f32>(0.,0.,0.,1.)"),
4355
            (comp_y, "y", "vec4<f32>(0.,0.,0.,1.)"),
4356
            (comp_z, "z", "vec4<f32>(0.,0.,0.,1.)"),
4357
            (comp_w, "w", "vec4<f32>(0.,0.,0.,1.)"),
4358
        ] {
4359
            let expr = ctx.eval(&m, expr);
4360
            assert!(expr.is_ok());
4361
            let expr = expr.unwrap();
4362
            assert_eq!(expr, format!("{}.{}", inner, op));
4363
        }
4364
    }
4365

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

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

4374
        let atan2 = m.atan2(x, y);
4375
        let cross = m.cross(x, y);
4376
        let dist = m.distance(x, y);
4377
        let dot = m.dot(x, y);
4378
        let min = m.min(x, y);
4379
        let max = m.max(x, y);
4380
        let step = m.step(x, y);
4381
        let vec4_xyz_w = m.vec4_xyz_w(x, z);
4382

4383
        let property_layout = PropertyLayout::default();
4384
        let particle_layout = ParticleLayout::default();
4385
        let mut ctx =
4386
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4387

4388
        for (expr, op) in [
4389
            (atan2, "atan2"),
4390
            (cross, "cross"),
4391
            (dist, "distance"),
4392
            (dot, "dot"),
4393
            (min, "min"),
4394
            (max, "max"),
4395
            (step, "step"),
4396
        ] {
4397
            let expr = ctx.eval(&m, expr);
4398
            assert!(expr.is_ok());
4399
            let expr = expr.unwrap();
4400
            assert_eq!(
4401
                expr,
4402
                format!(
4403
                    "{}(particle.{}, vec3<f32>(1.,1.,1.))",
4404
                    op,
4405
                    Attribute::POSITION.name(),
4406
                )
4407
            );
4408
        }
4409

4410
        {
4411
            let expr = ctx.eval(&m, vec4_xyz_w);
4412
            assert!(expr.is_ok());
4413
            let expr = expr.unwrap();
4414
            let z = ctx.eval(&m, z).unwrap();
4415
            assert_eq!(
4416
                expr,
4417
                format!("vec4(particle.{}, {})", Attribute::POSITION.name(), z)
4418
            );
4419
        }
4420
    }
4421

4422
    #[test]
4423
    fn ternary_expr() {
4424
        let mut m = Module::default();
4425

4426
        let x = m.attr(Attribute::POSITION);
4427
        let y = m.lit(Vec3::ONE);
4428
        let z = m.lit(Vec3::splat(2.));
4429
        let t = m.lit(0.3);
4430
        let a = m.lit(-4.2);
4431
        let b = m.lit(53.09);
4432

4433
        let mix = m.mix(x, y, t);
4434
        let clamp = m.clamp(x, y, z);
4435
        let smoothstep = m.smoothstep(x, y, x);
4436
        let vecthree = m.vec3(a, b, t);
4437

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

4443
        for (expr, op, third) in [
4444
            (mix, "mix", t),
4445
            (clamp, "clamp", z),
4446
            (smoothstep, "smoothstep", x),
4447
        ] {
4448
            let expr = ctx.eval(&m, expr);
4449
            assert!(expr.is_ok());
4450
            let expr = expr.unwrap();
4451
            let third = ctx.eval(&m, third).unwrap();
4452
            assert_eq!(
4453
                expr,
4454
                format!(
4455
                    "{}(particle.{}, vec3<f32>(1.,1.,1.), {})",
4456
                    op,
4457
                    Attribute::POSITION.name(),
4458
                    third
4459
                )
4460
            );
4461
        }
4462

4463
        {
4464
            let expr = ctx.eval(&m, vecthree);
4465
            assert!(expr.is_ok());
4466
            let expr = expr.unwrap();
4467
            let a = ctx.eval(&m, a).unwrap();
4468
            let b = ctx.eval(&m, b).unwrap();
4469
            let t = ctx.eval(&m, t).unwrap();
4470
            assert_eq!(expr, format!("vec3({}, {}, {})", a, b, t));
4471
        }
4472
    }
4473

4474
    #[test]
4475
    fn cast_expr() {
4476
        let mut m = Module::default();
4477

4478
        let x = m.attr(Attribute::POSITION);
4479
        let y = m.lit(IVec2::ONE);
4480
        let z = m.lit(0.3);
4481
        let w = m.lit(false);
4482

4483
        let cx = m.cast(x, VectorType::VEC3I);
4484
        let cy = m.cast(y, VectorType::VEC2U);
4485
        let cz = m.cast(z, ScalarType::Int);
4486
        let cw = m.cast(w, ScalarType::Uint);
4487

4488
        let property_layout = PropertyLayout::default();
4489
        let particle_layout = ParticleLayout::default();
4490
        let mut ctx =
4491
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4492

4493
        for (expr, cast, target) in [
4494
            (x, cx, ValueType::Vector(VectorType::VEC3I)),
4495
            (y, cy, VectorType::VEC2U.into()),
4496
            (z, cz, ScalarType::Int.into()),
4497
            (w, cw, ScalarType::Uint.into()),
4498
        ] {
4499
            let expr = ctx.eval(&m, expr);
4500
            assert!(expr.is_ok());
4501
            let expr = expr.unwrap();
4502
            let cast = ctx.eval(&m, cast);
4503
            assert!(cast.is_ok());
4504
            let cast = cast.unwrap();
4505
            assert_eq!(cast, format!("{}({})", target.to_wgsl_string(), expr));
4506
        }
4507
    }
4508

4509
    #[test]
4510
    fn attribute_pointer() {
4511
        let mut m = Module::default();
4512
        let x = m.attr(Attribute::POSITION);
4513

4514
        let property_layout = PropertyLayout::default();
4515
        let particle_layout = ParticleLayout::default();
4516
        let mut ctx =
4517
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4518

4519
        let res = ctx.eval(&m, x);
4520
        assert!(res.is_ok());
4521
        let xx = res.ok().unwrap();
4522
        assert_eq!(xx, format!("particle.{}", Attribute::POSITION.name()));
4523

4524
        // Use a different context; it's invalid to reuse a mutated context, as the
4525
        // expression cache will have been generated with the wrong context.
4526
        let mut ctx =
4527
            ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout)
4528
                .with_attribute_pointer();
4529

4530
        let res = ctx.eval(&m, x);
4531
        assert!(res.is_ok());
4532
        let xx = res.ok().unwrap();
4533
        assert_eq!(xx, format!("(*particle).{}", Attribute::POSITION.name()));
4534
    }
4535

4536
    #[test]
4537
    #[should_panic]
4538
    fn invalid_cast_vector_to_scalar() {
4539
        let mut m = Module::default();
4540
        let x = m.lit(Vec2::ONE);
4541
        let _ = m.cast(x, ScalarType::Float);
4542
    }
4543

4544
    #[test]
4545
    #[should_panic]
4546
    fn invalid_cast_matrix_to_scalar() {
4547
        let mut m = Module::default();
4548
        let x = m.lit(Value::Matrix(Mat4::ZERO.into()));
4549
        let _ = m.cast(x, ScalarType::Float);
4550
    }
4551

4552
    #[test]
4553
    #[should_panic]
4554
    fn invalid_cast_matrix_to_vector() {
4555
        let mut m = Module::default();
4556
        let x = m.lit(Value::Matrix(Mat4::ZERO.into()));
4557
        let _ = m.cast(x, VectorType::VEC4F);
4558
    }
4559

4560
    #[test]
4561
    #[should_panic]
4562
    fn invalid_cast_scalar_to_matrix() {
4563
        let mut m = Module::default();
4564
        let x = m.lit(3.);
4565
        let _ = m.cast(x, MatrixType::MAT3X3F);
4566
    }
4567

4568
    #[test]
4569
    #[should_panic]
4570
    fn invalid_cast_vector_to_matrix() {
4571
        let mut m = Module::default();
4572
        let x = m.lit(Vec3::ZERO);
4573
        let _ = m.cast(x, MatrixType::MAT2X4F);
4574
    }
4575

4576
    #[test]
4577
    fn cast_expr_new() {
4578
        let mut m = Module::default();
4579

4580
        let x = m.attr(Attribute::POSITION);
4581
        let c = CastExpr::new(x, VectorType::VEC3F);
4582
        assert_eq!(c.value_type(), ValueType::Vector(VectorType::VEC3F));
4583
        assert_eq!(c.is_valid(&m), Some(true));
4584

4585
        let x = m.attr(Attribute::POSITION);
4586
        let c = CastExpr::new(x, ScalarType::Bool);
4587
        assert_eq!(c.value_type(), ValueType::Scalar(ScalarType::Bool));
4588
        assert_eq!(c.is_valid(&m), Some(false)); // invalid cast vector -> scalar
4589

4590
        let p = m.add_property("my_prop", 3.0.into());
4591
        let y = m.prop(p);
4592
        let c = CastExpr::new(y, MatrixType::MAT2X3F);
4593
        assert_eq!(c.value_type(), ValueType::Matrix(MatrixType::MAT2X3F));
4594
        assert_eq!(c.is_valid(&m), None); // properties' value_type() is unknown
4595
    }
4596

4597
    #[test]
4598
    fn side_effect() {
4599
        let mut m = Module::default();
4600

4601
        // Adding the same cloned expression with side effect to itself should yield
4602
        // twice the value, and not two separate evaluations of the expression.
4603
        // CORRECT:
4604
        //   let r = frand();
4605
        //   r + r
4606
        // INCORRECT:
4607
        //   frand() + frand()
4608

4609
        let r = m.builtin(BuiltInOperator::Rand(ScalarType::Float.into()));
4610
        let r2 = r;
4611
        let r3 = r2;
4612
        let a = m.add(r, r2);
4613
        let b = m.mix(r, r2, r3);
4614
        let c = m.abs(a);
4615

4616
        {
4617
            let property_layout = PropertyLayout::default();
4618
            let particle_layout = ParticleLayout::default();
4619
            let mut ctx =
4620
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4621
            let value = ctx.eval(&m, a).unwrap();
4622
            assert_eq!(value, "(var0) + (var0)");
4623
            assert_eq!(ctx.main_code, "let var0 = frand();\n");
4624
        }
4625

4626
        {
4627
            let property_layout = PropertyLayout::default();
4628
            let particle_layout = ParticleLayout::default();
4629
            let mut ctx =
4630
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4631
            let value = ctx.eval(&m, b).unwrap();
4632
            assert_eq!(value, "mix(var0, var0, var0)");
4633
            assert_eq!(ctx.main_code, "let var0 = frand();\n");
4634
        }
4635

4636
        {
4637
            let property_layout = PropertyLayout::default();
4638
            let particle_layout = ParticleLayout::default();
4639
            let mut ctx =
4640
                ShaderWriter::new(ModifierContext::Update, &property_layout, &particle_layout);
4641
            let value = ctx.eval(&m, c).unwrap();
4642
            assert_eq!(value, "abs((var0) + (var0))");
4643
            assert_eq!(ctx.main_code, "let var0 = frand();\n");
4644
        }
4645
    }
4646

4647
    // #[test]
4648
    // fn serde() {
4649
    //     let v = Value::Scalar(3.0_f32.into());
4650
    //     let l: LiteralExpr = v.into();
4651
    //     assert_eq!(Ok(v), l.eval());
4652
    //     let s = ron::to_string(&l).unwrap();
4653
    //     println!("literal: {:?}", s);
4654
    //     let l_serde: LiteralExpr = ron::from_str(&s).unwrap();
4655
    //     assert_eq!(l_serde, l);
4656

4657
    //     let b: ExprHandle = Box::new(l);
4658
    //     let s = ron::to_string(&b).unwrap();
4659
    //     println!("boxed literal: {:?}", s);
4660
    //     let b_serde: ExprHandle = ron::from_str(&s).unwrap();
4661
    //     assert!(b_serde.is_const());
4662
    //     assert_eq!(b_serde.to_wgsl_string(), b.to_wgsl_string());
4663

4664
    //     let v0 = Value::Scalar(3.0_f32.into());
4665
    //     let v1 = Value::Scalar(2.5_f32.into());
4666
    //     let l0: LiteralExpr = v0.into();
4667
    //     let l1: LiteralExpr = v1.into();
4668
    //     let a = l0 + l1;
4669
    //     assert!(a.is_const());
4670
    //     assert_eq!(Ok(Value::Scalar(5.5_f32.into())), a.eval());
4671
    //     let s = ron::to_string(&a).unwrap();
4672
    //     println!("add: {:?}", s);
4673
    //     let a_serde: AddExpr = ron::from_str(&s).unwrap();
4674
    //     println!("a_serde: {:?}", a_serde);
4675
    //     assert_eq!(a_serde.left.to_wgsl_string(), l0.to_wgsl_string());
4676
    //     assert_eq!(a_serde.right.to_wgsl_string(), l1.to_wgsl_string());
4677
    // }
4678
}
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