The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Added a
TextureLayout::get_slot_by_name()
helper to retrieve the index of a texture slot.
- Compatible with Bevy 0.15
- MSRV updated to 1.82 to follow Bevy.
- Renamed
Module::add_texture()
toadd_texture_slot()
for clarity. The actual texture is bound to the slot through theMaterialEffect
component.
- Fixed a panic in the init compute shader when using trails and ribbons, affecting some combinations of graphics API (notably Vulkan) and GPU devices only. (#399)
- You can now call
EffectAsset::mesh
to change the mesh that a particle will use from the default quad to a custom mesh of your choosing. - A
size3
attribute has been added to allow controlling the size in 3D. - Vertex normals are available to modifiers via the normal shader variable, as long as they call the new RenderContext::set_needs_normal method.
- Added a new
serde
feature, and moved allSerialization
andDeserialization
derives under that feature. This allows disabling support on wasm, wheretypetag
is not available. The feature is enabled by default, so the behavior doesn't change on non-wasm targets. - Added support for WebAssembly (
wasm
). This requires disabling theserde
feature. (#41) - Added support for sampling textures in the Expression API via the
TextureSampleExpr
expression. Currently only supports color textures (no depth texture) and only sampling via WGSLtextureSample()
. (#355) - Added a new
EffectMaterial
component holding the actual textures to bind to the various slots of aModule
. - Added a new
Module::add_texture()
function to declare a new texture slot in a module. - Particle trails and ribbons can now be initialized with init modifiers.
- The
SetSizeModifier
now takes a 3D size gradient instead of a 2D one. - The
ParticleTextureModifier
doesn't directly hold the texture handle to use anymore. Instead, it holds an expression handle to the texture slot defined in theModule
of the effect. This allows dynamically changing the texture sampled. RenderModifier::apply_render()
now returns aResult<String, ExprError>
.- Several API changes occurred around the cloning of particles for trails and ribbons. See also the migration guide (v0.12 -> v0.13).
EffectSpawner
is now wrapped into a newEffectInitializers
. Each effect group has anEffectInitializer
, which can either be anEffectSpawner
(CPU spawning) or anEffectCloner
(GPU particle cloning).EffectAsset::new()
takes again a single capacity argument for the default first group. Other groups are incrementally added withwith_trails()
andwith_ribbons()
, specifying their respective capacity via those functions.- The age (
Attribute::AGE
) and lifetime (Attribute::LIFETIME
) of cloned particles can no longer be assigned manually; instead it's set via an argument toEffectAsset::with_trails()
andEffectAsset::with_ribbons()
, and cannot be modified anymore with expressions (and properties in particular). - Renamed the
tick_spawners()
system intotick_initializers()
for clarity.
- The
CloneModifier
was removed. Cloning is now controlled by theEffectCloner
, hosted by theEffectInitializers
component. - The
RibbonModifier
was removed. UseEffectAsset::with_ribbons()
to create ribbons.
- Fixed a bug where the generated render shader was declaring a binding as
storage<read>
(read-only) but theSpawner
struct contained anatomic<i32>
, which requires write access. TheSpawner
struct is now conditionally defining that field asi32
instead. - Fixed a race condition in ribbons leading to visual artifacts (particles linked to other unrelated particles). (#376)
- Fix regression building with
rustc 1.79
, which is the Bevy MSRV. (#360)
- Fix new
rustc 1.80
linter error breaking the crate build. (#351)
- Added
BuiltInOperator::IsAlive
exposing the current state of theis_alive
particle flag, which represents whether a particle is still alive or will be deallocated at the end of the update pass.
- Compatible with Bevy 0.14.
- Prevented an error from being emitted when a GPU pipeline is still being created. (#341)
- Fixed a panic when a particle effect as an invalid asset handle. (#343)
- Added a new
ScreenSpaceSizeModifier
which negates the effect of perspective projection, and makes the particle's size a pixel size in screen space, instead of a Bevy world unit size. This replaces the hard-coded behavior previously available on theSetSizeModifier
. - Added a new
ConformToSphereModifier
acting as an attractor applying a force toward a point (sphere center) to all particles in range, and making particles conform ("stick") to the sphere surface. - Added
vec2
andvec3
functions that allow construction of vectors from dynamic parts. - Added missing
VectorValue::as_uvecX()
andimpl From<UVecX> for VectorValue
for allX=2,3,4
. - Added a new
ShaderWriter
helper, common to the init and update modifier contexts, and which replaces theInitContext
andUpdateContext
. - Added a simple
impl Display for ModifierContext
. - Added
Modifier::apply()
which replaces the now deletedInitModifier::apply_init()
andUdpdateModifier::apply_update()
. - Added
RenderModifier::as_modifier()
to upcast aRenderModifier
to aModifier
. - Added
RenderModifier::boxed_render_clone()
to clone aRenderModifier
into a boxed self (instead of aBoxedModifier
, as we can't upcast fromBox<dyn RenderModifier>
toBox<dyn Modifier>
). Addedimpl Clone for Box<dyn RenderModifier>
based on this. - Added
EffectAsset::add_modifier()
to add a pre-boxedModifier
(so, aBoxedModifier
) to the init or update context, and addedEffectAsset::add_render_modifier()
to add a pre-boxedRenderModifier
to the render context. - Added
sqrt
andinverseSqrt
expressions to the Expression API. - Added
Hash
derive forProperty
. - Added
Module::add_property()
,Module::get_property()
, andModule::properties()
. - Added a new
PropertyHandle
, which is toProperty
what the existingExprHandle
is toExpr
: a unique handle into a owningModule
. - Added a new
RoundModifier
that allows round particles to be constructed without having to create a texture. - Added a new
trace
feature enabling somespan_info!()
profiling annotations. - Added the ability to make particle trails, via the
CloneModifier
and the addition of separate particle groups. See theworms.rs
example for usage. - Added the ability to stitch trail particles together to form ribbons with the
RibbonModifier
.
ExprHandle
is now#[repr(transparent)]
, which guarantees thatOption<ExprHandle>
has the same size asExprHandle
itself (4 bytes).EffectProperties::set_if_changed()
now returns theMut
variable it takes as input, to allow subsequent calls.VectorValue::new_uvecX()
now take aUVecX
instead of individual components, like for all other scalar types.- Merged the
InitModifier
andUpdateModifier
traits into theModifier
subtrait; see other changelog entries for details. This helps manage modifiers in a unified way, and generally simplifies writing and maintain modifiers compatible with both the init and update contexts. EffectAsset::init()
andEffectAsset::update()
now take aModifier
-bound type, and validate itsModifierContext
is compatible (and panics if not).EffectAsset::render()
now panics if the modifier is not compatible with theModifierContext::Render
. Note that this indicates a malformed render modifier, because all objects implementingRenderModifier
must includeModifierContext::Render
in theirModifier::context()
.- Improved the serialization format to reduce verbosity, by making the following types
#[serde(transparent)]
:ExprHandle
,LiteralExpr
,PropertyExpr
. - Moved properties from
EffectAsset
toModule
. Module::prop()
andPropertyExpr::new()
now take aPropertyHandle
instead of a property name (string).- The following modifiers'
via_property()
function now takes aPropertyHandle
instead of a property name:AccelModifier
,RadialAccelModifier
,TangentAccelModifier
. Expr
is nowCopy
, making it even more lightweight.ExprWriter::prop()
now panics if the property doesn't exist. This ensures the createdWriterExpr
is well-formed, which was impossible to validate at expression write time previously.- Effects rendered with
AlphaMode::Mask
now write to the depth buffer. Other effects continue to not write to it. This fixes particle flickering for alpha masked effects only. - Bind groups for effect rendering are now created in a separate system in the
EffectSystems::PrepareBindGroups
set, itself part of Bevy'sRenderSet::PrepareBindGroups
. They're also cached, which increases the performance of rendering many effects. - Merged the init and update pass bind groups for the particle buffer and associated resources in
EffectBfufer
. The new unified resources use the_sim
(simulation) suffix. CompiledParticleEffect
now holds a strong handle to the sameEffectAsset
as theParticleEffect
it's compiled from. This ensures the asset is not unloaded while in use during the frame. To allow anEffectAsset
to unload, clear the handle of theParticleEffect
, then allow theCompiledParticleEffect
to observe the change and clear its own handle too.- The
EffectProperties
component is now mandatory, and has been added to theParticleEffectBundle
. (#309)
- Removed the
screen_space_size
field from theSetSizeModifier
. Use the newScreenSpaceSizeModifier
to use a screen-space size. - Removed the built-in
ForceFieldSource
and associatedForceFieldModifier
. Use the newConformToSphereModifer
instead. The behavior might change a bit as the conforming code is not strictly identical; use theforce_field.rs
example with theexamples_world_inspector
feature to tweak the parameters in real time and observe how they work and change the effect. - Removed
InitContext
andUpdateContext
; they're replaced withShaderWriter
. - Removed
InitModifer
andUpdateModifer
. Modifiers for the init and update contexts now only need to implement the baseModifier
trait. - Removed downcast methods from
Modifier
(as_init()
,as_init_mut()
,as_update()
,as_update_mut()
). - Removed the various helper macros
impl_mod_xxx!()
to implement modifier traits; simply implement the trait by hand instead. - Removed
EffectAsset::with_property()
andEffectAsset::add_property()
; useModule::add_property()
instead. PropertyExpr
doesn't implement anymoreToWgslString
norFrom<String>
. To create aPropertyExpr
, you need a validPropertyHandle
, which would have been created viaModule::add_property()
with a default value enforcing a type. This ensures property expressions are well-formed on creation, and cannot reference non-existent properties.- Removed
ParticleEffect::spawner
, which was used as a per-instance override ofEffectAsset::spawner
. Manual spawn anEffectSpawner
component with overridden values to achieve the same feature.
- Fixed a panic in rendering randomly occurring when no effect is present.
- Fixed invalid WGSL being generated for large
u32
values. - Fixed particle flickering, only for particles rendered through the
AlphaMask3d
render pass (EffectAsset::alpha_mode == AlphaMode::Mask
). (#183) - Fixed invalid layout of all
mat3xR<f32>
returned byMatrixValue::as_bytes()
, which was missing padding. (#310) - Fixed a regression where declaring properties but not adding an
EffectProperties
component would prevent properties from being uploaded to GPU. TheEffectProperties
component is now mandatory, even if the effect doesn't use any property. However there's still no GPU resource allocated if no property is used. (#309) - Fixed the missing PRNG seeding per particle effect instance in the update pass. (#333)
- Compatible with Bevy 0.13
- Moved most shared WGSL code into an import module
vfx_common.wgsl
. This requires usingnaga_oil
for import resolution, which in turns meansnaga
andnaga_oil
are now dependencies ofbevy_hanabi
itself.
- Fixed a bug where particle attributes used in the context of a function would emit invalid code. (#275)
- Fixed a bug where the screen-space size of particles was not correctly computed, leading to small variations in size. The new code correctly sizes particles based on a screen pixel size. This may increase the actual size of particles, if a larger size had been previously used to compensate the error introduced by this bug. (#269)
- Fixed a bug where screen-space size ignored the particle's local orientation. (#269)
- Added a new
EffectProperties
component holding the runtime values for all properties of a singleParticleEffect
instance. This component can be added manually to the sameEntity
holding theParticleEffect
if you want to set initial values different from the default ones declared in theEffectAsset
. Otherwise Hanabi will add the component automatically. - Added a new
EffectSystems::UpdatePropertiesFromAsset
set running in thePostUpdate
schedule. During this set, Hanabi automatically updates allEffectProperties
if the properties declared in the underlyingEffectAsset
changed. - Added
OrientModifier::rotation
, an optional expression which allows rotating the particle within its oriented plane. The actual meaning depends on theOrientMode
used. (#258) - Added custom float attributes, which have no predefined meaning, but instead can be used to store any per-particle value.
Attribute::F32_0
toAttribute::F32_3
for scalarf32
attributes.Attribute::F32X2_0
toAttribute::F32X2_3
for vectorvec2<f32>
attributes.Attribute::F32X3_0
toAttribute::F32X3_3
for vectorvec3<f32>
attributes.Attribute::F32X4_0
toAttribute::F32X4_3
for vectorvec4<f32>
attributes.
- Added 4 new expressions for packing and unpacking a
vec4<f32>
into au32
:pack4x8snorm
,pack4x8unorm
,unpack4x8snorm
,unpack4x8unorm
. This is particularly useful to convert betweenCOLOR
(u32
) andHDR_COLOR
(vec4<f32>
). See thebillboard.rs
example for a use case. (#259)
- Properties of an effect have been moved from
CompiledParticleEffect
to a newEffectProperties
component. This splits the semantic of theCompiledParticleEffect
, which is purely an internal optimization, from the list of properties stored inEffectProperties
, which is commonly accessed by the user to assign new values to properties. - Thanks to the split of properties into
EffectProperties
, change detection now works on properties, and uploads to GPU will only occur when change detection triggered on the component. Previously properties were re-uploaded each frame to the GPU even if unchanged. - Effect properties are now reflected (via the new
EffectProperties
component). Attribute::ALL
is now private; useAttribute::all()
instead.- Exposed publicly the
attributes
andproperties
modules, and documented them.
- Added a new
OrientModifier
and itsOrientMode
, allowing various modes of particle orienting during the rendering phase. - Added
SimulationSpace::Local
to simulate particles in local effect space, before rendering them with theGlobalTransform
of the effect's entity. - Add access to
ModifierContext
andParticleLayout
from theEvalContext
when evaluating modifiers. - Added
SimulationSpace::eval()
to evaluate a context-specific expression allowing to transform the particles to the proper simulation space. - Added a few more functions to
Gradient<T>
:is_empty(
andlen()
which do as implied,from_keys()
which creates a new gradient from a key point iterator, andwith_key()
andwith_keys()
which append one or more keys to an existing gradient. - Added
AlphaMode
and the ability to render particles with alpha masking instead of alpha blending. This is controlled byEffectAsset::alpha_mode
and the newEffectAsset::with_alpha_mode()
helper. - Added a new
BuiltInOperator::AlphaCutoff
value and associated expression, which represent the alpha cutoff threshold when rendering an effect with alpha masking. Thebillboard
example has been updated to show how to use that value, and even dynamically change it with an expression. - Added
PropertyLayout::properties()
to iterate over the layout. - Added
From
implementations for the most common matrix types. - Added many more expressions to
Expr
. - Added
Expr::has_side_effect()
to determine if an expression has a side effect and therefore needs to be stored into a temporary local variable to avoid being evaluated more than once. - Added
EvalContext::make_local_var()
to generate a unique name for a variable local to anEvalContext
(generally, inside a function). - Added
EvalContext::push_stmt()
to emit a single statement prepended to the currently evaluating expression. This is useful to define temporary local variables for storing expressions with a side-effect. - Added
EvalContext::make_fn()
to create a function with a dedicatedEvalContext
, allowing to properly scope local variables and stored expression side effects. - Added
Module::try_get()
, similar toModule::get()
but returning aResult<&Expr, ExprError>
instead for convenience. - Added implementations of
ToWgslString
for the missing vector types (UVec2/3/4
,IVec2/3/4
,BVec2/3/4
). - Added new
CastExpr
expression to cast an operand expression to anotherValueType
. This adds a new variantExpr::Cast
too. - Added new
BinaryOperator::Remainder
to calculate the remainder (%
operator) of two expressions. - Added the
ImageSampleMapping
enum to determine how samples of the image of aParticleTextureModifier
are mapped to and modulated with the particle's base color. The new default behavior isImageSampleMapping::Modulate
, corresponding to a full modulate of all RGBA components. To restore the previous behavior, and use the Red channel of the texture as an opacity mask, setParticleTextureModifier::sample_mapping
toImageSampleMapping::ModulateOpacityFromR
. - Added new
FlipbookModifier
to treat the image of aParticleTextureModifier
as a grid sprite sheet, and allow rendering a sprite from that sheet. By animating the selected sprite, this creates a flipbook animation for the particle. - Added new
Attribute::SPRITE_INDEX
holding thei32
index of a sprite inside a sprite sheet texture. This is used with theFlipbookModifier
to render sprite-based animated particles.
- Compatible with Bevy 0.12
InitContext::new()
andUpdateContext::new()
now take an additional reference to theParticleLayout
of the effect.RenderContext
now implementsEvalContext
like the init and update contexts, and like them reference the module, particle layout, and property layout of the effect.Gradient<T>::new()
,Gradient<T>::constant()
, andGradient<T>::linear()
do not require theT: Default
trait bound anymore. The bound had been added by mistake, and is not necessary.Gradient<T>::new()
is now aconst fn
.Gradient<T>::constant()
andGradient<T>::linear()
do not attempt to perform linear searches anymore; instead they directly create theGradient<T>
object from scratch. This should not have any real consequence in practice though.- Changed
CompiledParticleEffect
to store aLayoutFlags
instead of individual boolean values, for convenience and consistency with the internal representation. - Changed
RenderContext
to implementEvalContext
. This allows render modifiers to use the expression API. PropertyLayout::generate_code()
has no more extra empty line at the end of the struct in the generated code.EvalContext::eval()
now caches the evaluation of anExprHandle
and guarantees that the evaluation is only ever performed once. This ensures that clonedExprHandle
making a same expression used in multiple places all reference the same evaluation, which is stored inside a local variable. This fixes an unexpected behavior where expressions with side effect likerand()
where emitted multiple times, leading to different values, even though a single expression was used (via cloned handles). To restore the old behavior, simply generating separate expressions from aModule
or anExprWriter
instead of cloning and reusing a sameExprHandle
.- The default texture sampling mode for
ParticleTextureModifier
is now a full RGBA modulate. SeeImageSampleMapping
for details. UseImageSampleMapping::ModulateOpacityFromR
to restore the previous behavior.
- Removed the
BillboardModifier
; this is superseded by theOrientModifier { mode: OrientMode::ParallelCameraDepthPlane }
. - Removed the
OrientAlongVelocityModifier
; this is superseded by theOrientModifier { mode: OrientMode::AlongVelocity }
. - Removed
module()
andexpr()
fromEvalContext
; the current module is now passed explicitly alongside theEvalContext
in functions such asEvalContext::eval()
.
- Render modifiers can now access simulation parameters (time, delta time) like in any other context.
- Fixed a panic in Debug builds when a
ParticleEffect
was marked as changed (for example, viaMut
) but the asset handle remained the same. (#228) - Fixed a bug in the
to_wgsl_string
impl ofMatrixType
that caused the first element to be added twice. - Fixed missing parentheses leading to incorrect operator order in the following modifiers depending on the expression(s) used:
SetPositionCircleModifier
SetPositionSphereModifier
SetVelocityCircleModifier
SetVelocitySphereModifier
SetVelocityTangentModifier
- Added
Gradient::linear()
helper method to produce a linear gradient between two values at keys0.
and1.
. EffectAsset
now owns aModule
field containing all theExpr
used by the effect's modifiers.- Added
ScalarType
,VectorType
, andMatrixType
to reprensent a scalar, vector, or matrix type, respectively. - Added
ValueType::is_numeric()
as well as query methods to determine the kind of value typeis_scalar()
/is_vector()
/is_matrix()
. - Added new Expression API:
Expr
,ExprHandle
,Module
,ExprWriter
,WriterExpr
. - Added new
EvalContext
trait representing the evaluation context of an expression, and giving access to the underlying expressionModule
and the property layout of the efect. The trait is implemented byInitContext
andUpdateContext
. - Added convenience method
PropertyLayout::contains()
to determine if a layout contains a property by name. - Added
SetSizeModifier::screen_space_size
andSizeOverLifetimeModifier::screen_space_size
boolean fields which change the behavior of the particle size to be expressed in screen-space logical pixels, independently of the camera projection. This enables creating particle effect with constant pixel size. Setscreen_space_size = false
to get the previous behavior. - Added a new utility trait
FloatHash
to allow implementingstd::cmp::Eq
andstd::hash::Hash
on floating-point variants ofCpuValue
(ex-spawn::Value
), making it possible to derivestd::hash::Hash
for any type usingCpuValue
. SetColorModifier
andSetSizeModifier
now implementstd::cmp::Eq
, thanks toCpuValue
itself implementing that trait for all floating point types (seeFloatHash
).- Added a new
KillSphereModifier
, similar toKillAabbModifier
but with a sphere shape.
- Renamed
spawn::Value
tospawn::CpuValue
to prevent confusion withgraph::Value
. The former is a legacy construct which should eventually be replaced by the latter (but cannot be yet). ValueType
is now one ofScalarType
/VectorType
/MatrixType
, allowing to represent a wider range of types, including booleans and matrices.graph::Value
is now one ofScalarValue
/VectorValue
/MatrixValue
, for consistency withValueType
.SimParams::dt
was renamed toSimParams::delta_time
for readability. Inside shaders,sim_params.dt
was also renamed tosim_params.delta_time
.InitContext
andUpdateContext
now hold a mutable reference to the underlyingModule
to allow modifiers to create newExpr
, and a read-only reference to the property layout of the effect.ModifierContext
is now a bitfield (flags), allowing modifiers to be used in multiple contexts. A prime example is the renamedSetAttributeModifier
which can be used both to initialize a particle attribute on spawn (if used in theInit
context), or update it during the simulation (if used in theUpdate
context). Adding the modifier to theEffectAsset
withinit()
orupdate()
determines in which context it's used.InitModifier::apply()
was renamed toInitModifier::apply_init()
to avoid a conflict with the other modifier traits in case a modifier implements multiple of them.UpdateModifier::apply()
was renamed toUpdateModifier::apply_update()
to avoid a conflict with the other modifier traits in case a modifier implements multiple of them.RenderModifier::apply()
was renamed toRenderModifier::apply_render()
to avoid a conflict with the other modifier traits in case a modifier implements multiple of them.InitModifier::apply_init()
andUpdateModifier::apply_update()
now return aResult<(), ExprError>
.- The following modifiers have been renamed, changing their
Init
prefix intoSet
to reflect the fact they can now be used both for particle init and update:InitAttributeModifier
->SetAttributeModifier
InitPositionCircleModifier
->SetPositionCircleModifier
InitPositionSphereModifier
->SetPositionSphereModifier
InitPositionCone3dModifier
->SetPositionCone3dModifier
InitVelocityCircleModifier
->SetVelocityCircleModifier
InitVelocitySphereModifier
->SetVelocitySphereModifier
InitVelocityTangentModifier
->SetVelocityTangentModifier
- All modifiers were changed to leverage the new Expression API. This means most fields are now of type
ExprHandle
instead of their previous numeric type (likeVec3
orf32
). Refer to the various examples and the migration guide to understand how to migrate those modifiers. Property::new()
takes adefault_value
argument asimpl Into<Value>
instead ofValue
. This should make it easier to call, without requiring any change to existing code.PropertyLayout::new()
takes aniter
argument asimpl IntoIterator
instead ofimpl Iterator
. This should make it easier to call, without requiring any change to existing code.- All
ParticleEffect
s are now compiled into aCompiledParticleEffect
as soon as Hanabi detects they were spawned (generally, same frame), irrespective of whether they are visible or not. Previously only effects withVisibility::Visible
where compiled, causing inconsistencies and panics when the effect was made visible later. - Shaders are now named (as required by Bevy 0.11), allowing better error reporting. Because Hanabi shaders are generated, the naming pattern used is
hanabi/<effect_name>_<hash>.wgsl
, where<effect_name>
is the value ofEffectAsset::name
, and<hash>
a unique hash depending on the content of the effect (modifiers and their values). - The content of the
modifier
module has been re-organized to group modifiers into submodules based on the part they modify. This means theinit
,update
, andrender
, sub-modules are gone, replaced with others. Because all modifiers are re-exported (flattened hierarchy), this generally should not cause any breaking change, but can occasionally create a breakage if some old code was qualifying them with their full module path.
- The
InitAgeModifier
,InitLifetimeModifier
, andInitSizeModifier
, were deleted. They're replaced with the more genericSetAttributeModifier
(ex-InitAttributeModifier
) which can set any attribute of a particle. Modifier::resolve_properties()
was a temporary workaround which has now been entirely made obsolete, and as a result has been deleted.- Deleted
DimValue
which was only used byInitSizeModifier
, and is more generally covered by the Expression API. - Deleted
ValueOrProperty
which is now unused, and is more generally covered by the Expression API.
- Fixed a bug where a
ParticleEffect
spawned hidden (withVisibility::Hidden
) would make Hanabi panic when made visible. Effects are now always compiled as soon as spawned. (#182) - Fixed the implementation of
std::hash::Hash
forSetColorModifier
andSetSizeModifier
, which were manually implemented and were not hashing the variant type of the value. This increased the risk of hash collision. The new implementation is derived thanks toCpuValue
itself now implementingstd::hash::Hash
, and therefore likely hashes to a different value than in the previous release.
-
Added
SetColorModifier
to set a per-particle color on spawning, which doesn't vary during the particle's lifetime. -
Added
SetSizeModifier
to set a per-particle size on spawning, which doesn't vary during the particle's lifetime. -
Added
EffectAsset::motion_integration
to configure the type of motion integration of the particles of a system. Using this field, the user can now completely disable motion integration, or perform it before the modifiers are applied. The default behavior remains to perform the motion integration after all modifiers have been applied (MotionIntegration::PostUpdate
). -
Added
InitAttributeModifier
to initialize any attribute of a particle to a given hard-coded value or CPU property value. -
Attribute
is now fully reflected (Reflect
andFromReflect
) as a struct with a "name" field containing its unique identifier, and a "default_value" field containing its defaultValue
. However note that attributes are immutable, so all mutating methods of theReflect
andFromReflect
traits are no-op, and will not produce the expected result. TheFromReflect
implementation expects aString
value, and usesAttribute::from_name()
to recover the corresponding built-in instance of the attribute with that name. -
Attribute
is now serializable. Attributes are serialized as a string containing their name, since the list of valid attributes is hard-coded and cannot be modified at runtime, and new custom attributes are not supported. The default value is not serialized. -
Init modifiers now also have access to the effect's properties.
-
Added
InitAttributeModifier
to initialize any attribute to a value or bind it to a property. This is especially useful with properties currently used implicitly likeAttribute::COLOR
andAttribute::HDR_COLOR
; now you can set the color of particles without the need to (ab)use aColorOverLifetimeModifier
with a uniform gradient. The binding with properties also allows dynamically changing the spawning color; see the updatedspawn_on_command.rs
example. -
Added 2 new components:
CompiledParticleEffect
caches the runtime data for a compiledParticleEffect
. This component is automatically managed by the library. Users can interact with it to manage the values of the properties for that particular effect instance.EffectSpawner
holds the runtime data for the spawner. Several fields and methods have been transfered from [EffectAsset::spawner
] and [ParticleEffect::spawer
] into this new component. The component is automatically spawned by the library. Users can interact with it to e.g. spawn a single burst of particle.
-
Added a new system set
EffectSystems::CompileEffects
running the newcompile_effects()
system in parallel of thetick_spawners()
system, during theCoreSet::PostUpdate
set.compile_effects()
updates theCompiledParticleEffect
of anEntity
based on the state of itsParticleEffect
.tick_spawners()
spawns if it doesn't exist then ticks theEffectSpawner
component located again on that sameEntity
.
Neither of those systems mutate the
ParticleEffect
component anymore (the change detection mechanism of Bevy components will not be triggered). -
Added
ParticleEffect::with_properties()
to define a set of properties from an iterator. Note however that theset_property()
method moved to theCompiledParticleEffect
. -
Added a
SimulationCondition
enum and anEffectAsset::simulation_condition
field allowing to control whether the effect is simulated while hidden (Visibility::Hidden
). (#166) -
Added a new
RadialAccelModifier
update modifier applying a per-particle acceleration in the radial direction defined as the direction from the modifier's specified origin to the particle current position. -
Added a new
TangentAccelModifier
update modifier applying a per-particle acceleration in the tangent direction defined as the cross product of the the modifier's specified rotation plane axis with the radial direction of the particle (calculated likeRadialAccelModifier
).
Attribute
is now a wrapper around the private type&'static AttributeInner
, and should be considered a "handle" which references an existing attribute and can be cheaply copied and compared. All mentions of&'static Attribute
from previous versions should be replaced withAttribute
(by value) to migrate to the new definition.- The
Attribute::AGE
andAttribute::LIFETIME
are not mandatory anymore, and are now only added to the particle layout of an effect if a modifier requires them.- Particles without an age are (obviously) not aged anymore.
- Particles without a lifetime are not reaped and therefore do not die from aging. They continue to age though (their
Attribute::AGE
is updated each frame).
- The
Attribute::POSITION
andAttribute::VELOCITY
are not mandatory anymore. They are required ifEffectAsset::motion_integration
is set to something other thanMotionIntegration::None
, but are not added automatically to all effects like they used to be, and instead require a modifier to explicitly insert them into the particle layout. Effects with a non-None
motion integration but missing either of those two attributes will emit a warning at runtime. Add a position or velocity initializing modifier to fix it. - The documentation for all modifiers has been updated to state which attribute(s) they require, if any. Modifiers insert the attributes they require into the particle layout of the effect the modifier is attached to.
Spawner
now contains the user-provided spawner configuration, which is serialized with theEffectAsset
. All runtime fields and related methods, which are not serialized, have been moved to the newEffectSpawner
components. Users should replace the following calls previously made onParticleEffect::maybe_spawner().unwrap()
to the newEffectSpawner
:set_active()
,with_active()
,is_active()
,reset()
. See e.g. thespawn_on_command.rs
example.- The former
Spawner::tick()
, now moved toEffectSpawner::tick()
, is now a public method. The method is still automatically called by thetick_spawners()
system. It's publicly exposed for testing and in case users want more control. - Moved
ParticleEffect::set_property()
toCompiledParticleEffect
to prevent triggering change detection onParticleEffect
which invalidates the cache. (#162) - The
Spawner
methodswith_active()
,set_active()
, andis_active()
, have been respectively renamed towith_starts_active()
,set_starts_active()
, andstarts_active()
. This highlights the fact the "active" state manipulated by those methods only refers to the initial state of the spawner. The current runtime active state is available from theEffectSpawner
once it's spawned bytick_spawners()
(after the first udpate).
- Deleted the following unused types:
EffectMaterial
,EffectMaterialUniformData
,EffectMaterialPlugin
,GpuEffectMaterial
.
- Fixed a bug where using
ParticleEffect::with_spawner()
would prevent properties from initializing correctly. - Fixed a bug where the effect texture of the previously batched effect was incorrectly selected instead of the texture of the current effect. (#167)
- Fixed a bug on some GPUs (most notably, on macOS) where incorrect data padding was breaking simulation of all but the first effect. (#165)
- Fixed calls to
ParticleEffect::set_property()
being ignored if made before the particle effect has been updated once, due to properties not being resolved into theEffectAsset
until the effect is effectively compiled. Theset_property()
method has now moved to the newCompiledParticleEffect
, so cannot by design be made anymore before the effect is first updated. - Fixed
ParticleEffect::set_property()
invalidating the shader cache of the particle effect and causing a full shader recompile, which was impacting performance and defeating the point of using properties in the first place. The method has been moved toCompiledParticleEffect
. (#162) - Fixed a bug where hidden (
Visibility::Hidden
) but still active effects were simulated in the background despite the documentation stating they were not. The newly-addedSimulationCondition::Always
allows explicitly opting in to this behavior in case you were relying on it, but this fix otherwise prevent those effects from being simulated. This possibly relates to #67.
- Added an example
init.rs
showing the various kinds of position initializing modifiers.
- Renamed
PositionCone3dModifier
intoInitPositionCone3dModifier
, and removed the velocity initializing. To recover the older behavior, add an extraInitVelocitySphereModifier
to initialize the velocity likePositionCone3dModifier
used to do.
- Fixed a bug in
PositionCone3dModifier
where the translation of the emitter is applied twice. (#152)
- Added a
SimulationSpace
enum with a single valueGlobal
, in preparation of future support for local-space particle simulation. - Added
InitAgeModifier
to initialize the age of particles to a specific (possibly random) value instead of the default0.0
. - Added properties, named quantities associated with an
EffectAsset
and modifiable at runtime and assignable to values of modifiers. This enables dynamically modifying at runtime any property-based modifier value. Other non-property-based values are assumed to be constant, and are optimized by hard-coding them into the various shaders. UseEffectAsset::with_property()
andEffectAsset::add_property()
to define a new property before binding it to a modifier's value. Thespawn.rs
example demonstrates this with the acceleration of theAccelModifier
. - Added particle
Attribute
s, individual items holding a single per-particle scalar or vector value. Composed together, the attributes form the particle layout, which defines at runtime the set of per-particle values used in the simulation. This change marks a transition from the previous design where the particle attributes were hard-coded to be the position, velocity, age, and lifetime of the particle. With this change, a collection of various attributes is available for modifiers to manipulate. This ensures flexibility in customizing while retaining a minimal per-particle memory footprint for a given effect. Note that [Attribute
]s are all built-in; a custom [Attribute
] cannot be used. - Added a new
AabbKillModifier
which kills all particles entering or exiting an AABB. Theforce_field.rs
example shows an example of each variant. - Added a new
OrientAlongVelocityModifier
which orients the local X axis of particles alongside their velocity, and builds a local Z axis perpendicular to the camera view's plane. The local Y axis is derived to form an orthonormal frame.
- Switch to Bevy v0.10.
- The
ParticleLifetimeModifier
was renamed toInitLifetimeModifier
for clarity, and itslifetime
field is now aValue<f32>
to allow randomizing per particle. - Effects no longer have a default particle lifetime of 5 seconds. Instead an explicit lifetime must be set with the
InitLifetimeModifier
. Failure to set the lifetime will trigger a warning at runtime, and particles will default to a lifetime of zero and instantly die. - The
PositionSphereModifier
was previously initializing both the position and velocity of particles. It has been split into anInitPositionSphereModifier
to initialize the position, andInitVelocitySphereModifier
to initialize the velocity. - The
PositionCircleModifier
was previously initializing both the position and velocity of particles. It has been split into anInitPositionCircleModifier
to initialize the position, andInitVelocityCircleModifier
to initialize the velocity. - Effects no longer have a default initial position similar to the ex-
PositionSphereModifier
. Add anInitPositionSphereModifier
and anInitVelocitySphereModifier
explicitly to an effect to restore the previous behavior, with a center ofVec3::ZERO
, a radius of1.
, a dimension ofShapeDimension::Volume
, and a speed of2.
. - The acceleration of the
AccelModifier
is now property-based. To dynamically change the acceleration at runtime, assign its value to an effect property withAccelModifier::via_property()
. Otherwise a constant value built withAccelModifier::constant()
can be used, and will be optimized in the update shader. See thespawn.rs
example. - All modifiers are now fully reflected (derive both
Reflect
andFromReflect
) and de/serializable. They're serialized as enums, using thetypetag
crate. ShapeDimension
now derivesDebug
and is fully reflected (derives bothReflect
andFromReflect
) and de/serializable.Value<T>
now requiresT: FromReflect
, and itself derives bothReflect
andFromReflect
.- Consequence of
Value<T>
being fully reflected, several fields onSpawner
are now fully reflected too and not ignored anymore. - The conforming to sphere feature of
ForceFieldModifier
is now applied before the Euler integration updating the particle position. This may result is tiny deviations from the previous behavior, as the particle position will not strictly conform to the sphere at the end of the step. However the delta should be very small, and no visible difference is expected in practice. - Changed the
instancing
example to allow removing particles with the BACKSPACE key in addition of the DELETE one, mainly for useability on macOS. - Changed the
lifetime
example to render particles with a colored gradient, to make the lifetime effect more clear. - The library builds with
#[deny(dead_code)]
.
- Deleted
InitLayout
,UpdateLayout
, andRenderLayout
. Init, update, and render modifiers are now directly applying themselves to theInitContext
,UpdateContext
, andRenderContext
, respectively.
- Fixed a bug breaking effect simulation after some effects are despawned and others are subsequently spawned. (#106)
- Fixed the
spawn
example failing to start on several devices due to the monitor resolution being larger than the maximum resolution imposed by the downlevel settings of WGPU. The downlevel settings are now disabled by default, and can be manually re-added for testing.
- Fix a panic on
unwrap()
after despawning N > 1 effects and re-spawning M < N effects. (#123)
- Changed the
instance.rs
example to spawn effects in random positions. Also added a (disabled) stress test which randomly spawns and despawns effects quickly to uncover bugs more easily.
- Made
Gradient<T>
reflected and serializable by implementingReflect
,FromReflect
,Serialize
, andDeserialize
.
- Fix (most common cases of) a bug where effects spawned after another effect was despawned will not work. This is a partial workaround; the bug can still trigger but under more rare conditions. (#106)
- Fix simulate compute jobs running once per view instead of once per frame. (#102)
- Fix 2D rendering not using indirect (GPU-driven) rendering.
- Fix particles being reset after another effect is despawned. (#117)
- Removed
MinMaxRect
in favor of Bevy's ownRect
type. - Removed
Resource
derive fromEffectAsset
, which made little sense on an asset.
- Add support for HDR cameras (
Camera::hdr == true
). - Add support for render layers (
RenderLayers
), allowing to select which camera(s) renders theParticleEffect
s. - Add support for linear drag force via the
LinearDragModifier
. This enables slowing down the particles over time.
- Fix a panic when running the plugin without any effect.
- Fix a bug in the way
BillboardModifier
was projecting the particle vertices onto the camera plane, producing some partial or total clipping of particles.
- Switch to Bevy v0.9.
- Disabled broken effect batching until #73 is fixed, to prevent triggering batching which breaks rendering.
- Switched to a new internal architecture, splitting the initializing of newly spawned particles from the updating of all alive particles, to achieve more consistent workload on the update compute. Added GPU-driven compute dispatch and rendering, which slighly improves performance and reduces CPU dependency/synchronization. This is mostly an internal change, but with the potential to unblock or facilitate several other issues. (#19)
- Removed
ParticleEffect::spawner()
from the public API, which was intended for internal use and is a bit confusing. - Renamed the oddly-named
RenderLayout::size_color_gradient
into the more understandableRenderLayout::lifetime_size_gradient
. - Renamed
PipelineRegistry
intoShaderCache
, and itsconfigure()
method intoget_or_insert()
, for clarity.
- Prevent
ShaderCache::get_or_insert()
from unnecessarily triggering change detection onAssets<Shader>
when the item is already in the cache.
- Respect user-defined MSAA setting by reading the value of
Msaa::samples
when building the render pipeline. (#59) - Fixed a bug in the effect cache causing a panic sometimes when effects are removed. (#60)
- Fixed a bug where an effect instance would be allocated overwriting another existing instance.
- Fixed a bug in the calculation of some GPU buffer binding causing a panic under some combination of effect capacity and spawn count. (#68)
- Added
PositionCone3dModifier
to spawn particles inside a truncated 3D cone. - All GPU profiling markers are now prefixed with
hanabi:
to make it easier to find Hanabi-related GPU resources.
- Moved all modifiers into a top-level
modifier
module, and further into someinit
,update
, andrender
sub-modules. - Added a
bevy_hanabi::prelude
containing most public types, to be used preferably overuse bevy_hanabi::*
. - Renamed
ForceFieldParam
toForceFieldSource
. - Renamed the
FFNUM
constant toForceFieldSource::MAX_SOURCES
. - Renamed
ForceFieldModifier::force_field
toForceFieldModifier::sources
.
- The orientation of the
Entity
of theParticleEffect
is now taken into account for spawning. (#42) - Ensure all GPU resources are deallocated when a
ParticleEffect
component is despawned. (#45)
EffectCacheId
is now private. It was exposed publicly by error, and cannot be used for anything in the public API anyway.
- Added
EffectAsset::z_layer_2d
andParticleEffect::z_layer_2d
to control the Z layer at which particles are rendered in 2D mode. Note that effects with different Z values cannot be batched together, which may negatively affect performance. - Added
BillboardModifier
to force the particles to face the camera.
- Switch to Bevy v0.8.
- Update spawners in a separate system
tick_spawners()
(label:EffectSystems::TickSpawners
) which runs in theCoreStage::PostUpdate
stage after the visibility system updated allComputedVisibility
, to allow skipping effect instances which are not visible. Spawners were previously ticked in the render extract phase.
- Added test-only feature
gpu_tests
active by default to enable tests requiring a working graphic adapter (GPU). This is disabled in most CI tests, except on Linux where the CPU-based Vulkan emulatorlavapipe
is used.
- Switch to Bevy v0.7.
- Changed features
2d
and3d
to be purely additive. They are now both active by default, allowing to render through both 2D and 3D cameras at the same time. Users can optionally select either of those exclusively via the--no-default-features --features='2d'
options (or similar for 3D), as an optimization for applications using only one of the two codepaths. - Tighter set of dependencies, removing the general
bevy/render
and instead depending onbevy/bevy_core_pipeline
andbevy/bevy_render
only.
- Fix missing
derive
feature inbytemuck
dependency occasionally causing build errors. - Fix a bug in spawner parameters alignment making the library crash on some GPUs. The spawner parameters are now properly aligned according to the device-dependent constraints queried at runtime. (#26)
- Add
SizeOverLifetimeModifier
. - Add
PositionCircleModifier
to allow spawning from a circle or disc. - Revamped spawning system:
SpawnMode
is gone;Spawner
s are constructed with associated functionsnew
,once
,rate
, andburst
.- Spawners can be reset with
Spawner::reset
. This gives control over when to spawn a burst of particles. - Spawners can be activated or deactivated with
Spawner::set_active
. ParticleEffectBundle
s can be initialized with a spawner withParticleEffectBundle::with_spawner
.
- Implemented
ToWgslFloat
forVec2
/Vec3
/Vec4
. - Implemented
ToWgslFloat
forValue<f32>
. - Derive-implemented
PartialEq
forValue<T>
andSpawner
. - Implemented randomization for randomized spawning parameters
- New force field effect:
- Add
ForceFieldModifier
to allow attraction or repulsion from point sources. - Add
ForceFieldParam
in both the modifiers and the particle update shader. - Add
force_field
example showcasing a repulsor, an attractor and the conforming to sphere functionality.
- Add
- Add rendering with a 2D camera.
- Renamed the
ToWgslFloat
trait intoToWgslString
, and itsto_float_string()
method intoto_wgsl_string()
. Also made the trait public. - Position modifiers now use
Value<f32>
for velocity to allow for random velocity. - Either the "3d" feature or the "2d" feature must be enabled.
- Fixed depth sorting of particles relative to opaque objects. Particles are now correctly hidden when behind opaque objects.
- Fixed truncation in compute workgroup count preventing update of some particles, and in degenerate cases (
capacity < 64
) completely disabling update. - Made the
GradientKey<T>::ratio
field private to avoid any modification viaGradient<T>::keys_mut()
which would corrupt the internal sorting of keys. - Fixed a bug where adding the
HanabiPlugin
to an app without spawning anyParticleEffect
would crash at runtime. (#9).
- Fix homepage link in
Cargo.toml
- Bevy 0.6.1 fixed build on nightly, thereby fixing docs.rs builds
Initial alpha version. Lots of things missing, but the barebone functionality is there. See the README.md for the list of planned and implemented features.