The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Added a new
ScreenSpaceSizeModifierwhich 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
ConformToSphereModifieracting 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
vec2andvec3functions that allow construction of vectors from dynamic parts. - Added missing
VectorValue::as_uvecX()andimpl From<UVecX> for VectorValuefor allX=2,3,4. - Added a new
ShaderWriterhelper, common to the init and update modifier contexts, and which replaces theInitContextandUpdateContext. - 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 aRenderModifierto aModifier. - Added
RenderModifier::boxed_render_clone()to clone aRenderModifierinto 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-boxedRenderModifierto the render context. - Added
sqrtandinverseSqrtexpressions to the Expression API. - Added
Hashderive forProperty. - Added
Module::add_property(),Module::get_property(), andModule::properties(). - Added a new
PropertyHandle, which is toPropertywhat the existingExprHandleis toExpr: a unique handle into a owningModule. - Added a new
RoundModifierthat allows round particles to be constructed without having to create a texture. - Added a new
tracefeature enabling somespan_info!()profiling annotations. - Added the ability to make particle trails, via the
CloneModifierand the addition of separate particle groups. See theworms.rsexample for usage. - Added the ability to stitch trail particles together to form ribbons with the
RibbonModifier.
ExprHandleis now#[repr(transparent)], which guarantees thatOption<ExprHandle>has the same size asExprHandleitself (4 bytes).EffectProperties::set_if_changed()now returns theMutvariable it takes as input, to allow subsequent calls.VectorValue::new_uvecX()now take aUVecXinstead of individual components, like for all other scalar types.- Merged the
InitModifierandUpdateModifiertraits into theModifiersubtrait; 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 itsModifierContextis 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 implementingRenderModifiermust includeModifierContext::Renderin theirModifier::context().- Improved the serialization format to reduce verbosity, by making the following types
#[serde(transparent)]:ExprHandle,LiteralExpr,PropertyExpr. - Moved properties from
EffectAssettoModule. Module::prop()andPropertyExpr::new()now take aPropertyHandleinstead of a property name (string).- The following modifiers'
via_property()function now takes aPropertyHandleinstead of a property name:AccelModifier,RadialAccelModifier,TangentAccelModifier. Expris nowCopy, making it even more lightweight.ExprWriter::prop()now panics if the property doesn't exist. This ensures the createdWriterExpris well-formed, which was impossible to validate at expression write time previously.- Effects rendered with
AlphaMode::Masknow 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::PrepareBindGroupsset, 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. CompiledParticleEffectnow holds a strong handle to the sameEffectAssetas theParticleEffectit's compiled from. This ensures the asset is not unloaded while in use during the frame. To allow anEffectAssetto unload, clear the handle of theParticleEffect, then allow theCompiledParticleEffectto observe the change and clear its own handle too.- The
EffectPropertiescomponent is now mandatory, and has been added to theParticleEffectBundle. (#309)
- Removed the
screen_space_sizefield from theSetSizeModifier. Use the newScreenSpaceSizeModifierto use a screen-space size. - Removed the built-in
ForceFieldSourceand associatedForceFieldModifier. Use the newConformToSphereModiferinstead. The behavior might change a bit as the conforming code is not strictly identical; use theforce_field.rsexample with theexamples_world_inspectorfeature to tweak the parameters in real time and observe how they work and change the effect. - Removed
InitContextandUpdateContext; they're replaced withShaderWriter. - Removed
InitModiferandUpdateModifer. Modifiers for the init and update contexts now only need to implement the baseModifiertrait. - 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. PropertyExprdoesn't implement anymoreToWgslStringnorFrom<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 anEffectSpawnercomponent 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
u32values. - Fixed particle flickering, only for particles rendered through the
AlphaMask3drender 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
EffectPropertiescomponent would prevent properties from being uploaded to GPU. TheEffectPropertiescomponent 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_oilfor import resolution, which in turns meansnagaandnaga_oilare now dependencies ofbevy_hanabiitself.
- 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
EffectPropertiescomponent holding the runtime values for all properties of a singleParticleEffectinstance. This component can be added manually to the sameEntityholding theParticleEffectif 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::UpdatePropertiesFromAssetset running in thePostUpdateschedule. During this set, Hanabi automatically updates allEffectPropertiesif the properties declared in the underlyingEffectAssetchanged. - Added
OrientModifier::rotation, an optional expression which allows rotating the particle within its oriented plane. The actual meaning depends on theOrientModeused. (#258) - Added custom float attributes, which have no predefined meaning, but instead can be used to store any per-particle value.
Attribute::F32_0toAttribute::F32_3for scalarf32attributes.Attribute::F32X2_0toAttribute::F32X2_3for vectorvec2<f32>attributes.Attribute::F32X3_0toAttribute::F32X3_3for vectorvec3<f32>attributes.Attribute::F32X4_0toAttribute::F32X4_3for 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.rsexample for a use case. (#259)
- Properties of an effect have been moved from
CompiledParticleEffectto a newEffectPropertiescomponent. 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
EffectPropertiescomponent). Attribute::ALLis now private; useAttribute::all()instead.- Exposed publicly the
attributesandpropertiesmodules, and documented them.
- Added a new
OrientModifierand itsOrientMode, allowing various modes of particle orienting during the rendering phase. - Added
SimulationSpace::Localto simulate particles in local effect space, before rendering them with theGlobalTransformof the effect's entity. - Add access to
ModifierContextandParticleLayoutfrom theEvalContextwhen 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
AlphaModeand the ability to render particles with alpha masking instead of alpha blending. This is controlled byEffectAsset::alpha_modeand the newEffectAsset::with_alpha_mode()helper. - Added a new
BuiltInOperator::AlphaCutoffvalue and associated expression, which represent the alpha cutoff threshold when rendering an effect with alpha masking. Thebillboardexample 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
Fromimplementations 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
ToWgslStringfor the missing vector types (UVec2/3/4,IVec2/3/4,BVec2/3/4). - Added new
CastExprexpression to cast an operand expression to anotherValueType. This adds a new variantExpr::Casttoo. - Added new
BinaryOperator::Remainderto calculate the remainder (%operator) of two expressions. - Added the
ImageSampleMappingenum to determine how samples of the image of aParticleTextureModifierare 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_mappingtoImageSampleMapping::ModulateOpacityFromR. - Added new
FlipbookModifierto treat the image of aParticleTextureModifieras 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_INDEXholding thei32index of a sprite inside a sprite sheet texture. This is used with theFlipbookModifierto render sprite-based animated particles.
- Compatible with Bevy 0.12
InitContext::new()andUpdateContext::new()now take an additional reference to theParticleLayoutof the effect.RenderContextnow implementsEvalContextlike 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: Defaulttrait 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
CompiledParticleEffectto store aLayoutFlagsinstead of individual boolean values, for convenience and consistency with the internal representation. - Changed
RenderContextto 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 anExprHandleand guarantees that the evaluation is only ever performed once. This ensures that clonedExprHandlemaking 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 aModuleor anExprWriterinstead of cloning and reusing a sameExprHandle.- The default texture sampling mode for
ParticleTextureModifieris now a full RGBA modulate. SeeImageSampleMappingfor details. UseImageSampleMapping::ModulateOpacityFromRto 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 theEvalContextin 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
ParticleEffectwas marked as changed (for example, viaMut) but the asset handle remained the same. (#228) - Fixed a bug in the
to_wgsl_stringimpl ofMatrixTypethat 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:
SetPositionCircleModifierSetPositionSphereModifierSetVelocityCircleModifierSetVelocitySphereModifierSetVelocityTangentModifier
- Added
Gradient::linear()helper method to produce a linear gradient between two values at keys0.and1.. EffectAssetnow owns aModulefield containing all theExprused by the effect's modifiers.- Added
ScalarType,VectorType, andMatrixTypeto 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
EvalContexttrait representing the evaluation context of an expression, and giving access to the underlying expressionModuleand the property layout of the efect. The trait is implemented byInitContextandUpdateContext. - Added convenience method
PropertyLayout::contains()to determine if a layout contains a property by name. - Added
SetSizeModifier::screen_space_sizeandSizeOverLifetimeModifier::screen_space_sizeboolean 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 = falseto get the previous behavior. - Added a new utility trait
FloatHashto allow implementingstd::cmp::Eqandstd::hash::Hashon floating-point variants ofCpuValue(ex-spawn::Value), making it possible to derivestd::hash::Hashfor any type usingCpuValue. SetColorModifierandSetSizeModifiernow implementstd::cmp::Eq, thanks toCpuValueitself implementing that trait for all floating point types (seeFloatHash).- Added a new
KillSphereModifier, similar toKillAabbModifierbut with a sphere shape.
- Renamed
spawn::Valuetospawn::CpuValueto prevent confusion withgraph::Value. The former is a legacy construct which should eventually be replaced by the latter (but cannot be yet). ValueTypeis now one ofScalarType/VectorType/MatrixType, allowing to represent a wider range of types, including booleans and matrices.graph::Valueis now one ofScalarValue/VectorValue/MatrixValue, for consistency withValueType.SimParams::dtwas renamed toSimParams::delta_timefor readability. Inside shaders,sim_params.dtwas also renamed tosim_params.delta_time.InitContextandUpdateContextnow hold a mutable reference to the underlyingModuleto allow modifiers to create newExpr, and a read-only reference to the property layout of the effect.ModifierContextis now a bitfield (flags), allowing modifiers to be used in multiple contexts. A prime example is the renamedSetAttributeModifierwhich can be used both to initialize a particle attribute on spawn (if used in theInitcontext), or update it during the simulation (if used in theUpdatecontext). Adding the modifier to theEffectAssetwithinit()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
Initprefix intoSetto reflect the fact they can now be used both for particle init and update:InitAttributeModifier->SetAttributeModifierInitPositionCircleModifier->SetPositionCircleModifierInitPositionSphereModifier->SetPositionSphereModifierInitPositionCone3dModifier->SetPositionCone3dModifierInitVelocityCircleModifier->SetVelocityCircleModifierInitVelocitySphereModifier->SetVelocitySphereModifierInitVelocityTangentModifier->SetVelocityTangentModifier
- All modifiers were changed to leverage the new Expression API. This means most fields are now of type
ExprHandleinstead of their previous numeric type (likeVec3orf32). Refer to the various examples and the migration guide to understand how to migrate those modifiers. Property::new()takes adefault_valueargument asimpl Into<Value>instead ofValue. This should make it easier to call, without requiring any change to existing code.PropertyLayout::new()takes aniterargument asimpl IntoIteratorinstead ofimpl Iterator. This should make it easier to call, without requiring any change to existing code.- All
ParticleEffects are now compiled into aCompiledParticleEffectas soon as Hanabi detects they were spawned (generally, same frame), irrespective of whether they are visible or not. Previously only effects withVisibility::Visiblewhere 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
modifiermodule 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
DimValuewhich was only used byInitSizeModifier, and is more generally covered by the Expression API. - Deleted
ValueOrPropertywhich is now unused, and is more generally covered by the Expression API.
- Fixed a bug where a
ParticleEffectspawned 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::HashforSetColorModifierandSetSizeModifier, 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 toCpuValueitself now implementingstd::hash::Hash, and therefore likely hashes to a different value than in the previous release.
-
Added
SetColorModifierto set a per-particle color on spawning, which doesn't vary during the particle's lifetime. -
Added
SetSizeModifierto set a per-particle size on spawning, which doesn't vary during the particle's lifetime. -
Added
EffectAsset::motion_integrationto 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
InitAttributeModifierto initialize any attribute of a particle to a given hard-coded value or CPU property value. -
Attributeis now fully reflected (ReflectandFromReflect) 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 theReflectandFromReflecttraits are no-op, and will not produce the expected result. TheFromReflectimplementation expects aStringvalue, and usesAttribute::from_name()to recover the corresponding built-in instance of the attribute with that name. -
Attributeis 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
InitAttributeModifierto initialize any attribute to a value or bind it to a property. This is especially useful with properties currently used implicitly likeAttribute::COLORandAttribute::HDR_COLOR; now you can set the color of particles without the need to (ab)use aColorOverLifetimeModifierwith a uniform gradient. The binding with properties also allows dynamically changing the spawning color; see the updatedspawn_on_command.rsexample. -
Added 2 new components:
CompiledParticleEffectcaches 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.EffectSpawnerholds 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::CompileEffectsrunning the newcompile_effects()system in parallel of thetick_spawners()system, during theCoreSet::PostUpdateset.compile_effects()updates theCompiledParticleEffectof anEntitybased on the state of itsParticleEffect.tick_spawners()spawns if it doesn't exist then ticks theEffectSpawnercomponent located again on that sameEntity.
Neither of those systems mutate the
ParticleEffectcomponent 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
SimulationConditionenum and anEffectAsset::simulation_conditionfield allowing to control whether the effect is simulated while hidden (Visibility::Hidden). (#166) -
Added a new
RadialAccelModifierupdate 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
TangentAccelModifierupdate 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).
Attributeis 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 Attributefrom previous versions should be replaced withAttribute(by value) to migrate to the new definition.- The
Attribute::AGEandAttribute::LIFETIMEare 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::AGEis updated each frame).
- The
Attribute::POSITIONandAttribute::VELOCITYare not mandatory anymore. They are required ifEffectAsset::motion_integrationis 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-Nonemotion 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.
Spawnernow 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 newEffectSpawnercomponents. 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.rsexample.- 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()toCompiledParticleEffectto prevent triggering change detection onParticleEffectwhich invalidates the cache. (#162) - The
Spawnermethodswith_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 theEffectSpawneronce 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 theEffectAssetuntil 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::Alwaysallows 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.rsshowing the various kinds of position initializing modifiers.
- Renamed
PositionCone3dModifierintoInitPositionCone3dModifier, and removed the velocity initializing. To recover the older behavior, add an extraInitVelocitySphereModifierto initialize the velocity likePositionCone3dModifierused to do.
- Fixed a bug in
PositionCone3dModifierwhere the translation of the emitter is applied twice. (#152)
- Added a
SimulationSpaceenum with a single valueGlobal, in preparation of future support for local-space particle simulation. - Added
InitAgeModifierto initialize the age of particles to a specific (possibly random) value instead of the default0.0. - Added properties, named quantities associated with an
EffectAssetand 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.rsexample demonstrates this with the acceleration of theAccelModifier. - Added particle
Attributes, 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
AabbKillModifierwhich kills all particles entering or exiting an AABB. Theforce_field.rsexample shows an example of each variant. - Added a new
OrientAlongVelocityModifierwhich 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
ParticleLifetimeModifierwas renamed toInitLifetimeModifierfor clarity, and itslifetimefield 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
PositionSphereModifierwas previously initializing both the position and velocity of particles. It has been split into anInitPositionSphereModifierto initialize the position, andInitVelocitySphereModifierto initialize the velocity. - The
PositionCircleModifierwas previously initializing both the position and velocity of particles. It has been split into anInitPositionCircleModifierto initialize the position, andInitVelocityCircleModifierto initialize the velocity. - Effects no longer have a default initial position similar to the ex-
PositionSphereModifier. Add anInitPositionSphereModifierand anInitVelocitySphereModifierexplicitly 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
AccelModifieris 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.rsexample. - All modifiers are now fully reflected (derive both
ReflectandFromReflect) and de/serializable. They're serialized as enums, using thetypetagcrate. ShapeDimensionnow derivesDebugand is fully reflected (derives bothReflectandFromReflect) and de/serializable.Value<T>now requiresT: FromReflect, and itself derives bothReflectandFromReflect.- Consequence of
Value<T>being fully reflected, several fields onSpawnerare now fully reflected too and not ignored anymore. - The conforming to sphere feature of
ForceFieldModifieris 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
instancingexample to allow removing particles with the BACKSPACE key in addition of the DELETE one, mainly for useability on macOS. - Changed the
lifetimeexample 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
spawnexample 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.rsexample 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
MinMaxRectin favor of Bevy's ownRecttype. - Removed
Resourcederive 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 theParticleEffects. - 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
BillboardModifierwas 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_gradientinto the more understandableRenderLayout::lifetime_size_gradient. - Renamed
PipelineRegistryintoShaderCache, 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::sampleswhen 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
PositionCone3dModifierto 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
modifiermodule, and further into someinit,update, andrendersub-modules. - Added a
bevy_hanabi::preludecontaining most public types, to be used preferably overuse bevy_hanabi::*. - Renamed
ForceFieldParamtoForceFieldSource. - Renamed the
FFNUMconstant toForceFieldSource::MAX_SOURCES. - Renamed
ForceFieldModifier::force_fieldtoForceFieldModifier::sources.
- The orientation of the
Entityof theParticleEffectis now taken into account for spawning. (#42) - Ensure all GPU resources are deallocated when a
ParticleEffectcomponent is despawned. (#45)
EffectCacheIdis now private. It was exposed publicly by error, and cannot be used for anything in the public API anyway.
- Added
EffectAsset::z_layer_2dandParticleEffect::z_layer_2dto 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
BillboardModifierto 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::PostUpdatestage 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_testsactive 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 emulatorlavapipeis used.
- Switch to Bevy v0.7.
- Changed features
2dand3dto 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/renderand instead depending onbevy/bevy_core_pipelineandbevy/bevy_renderonly.
- Fix missing
derivefeature inbytemuckdependency 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
PositionCircleModifierto allow spawning from a circle or disc. - Revamped spawning system:
SpawnModeis gone;Spawners 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. ParticleEffectBundles can be initialized with a spawner withParticleEffectBundle::with_spawner.
- Implemented
ToWgslFloatforVec2/Vec3/Vec4. - Implemented
ToWgslFloatforValue<f32>. - Derive-implemented
PartialEqforValue<T>andSpawner. - Implemented randomization for randomized spawning parameters
- New force field effect:
- Add
ForceFieldModifierto allow attraction or repulsion from point sources. - Add
ForceFieldParamin both the modifiers and the particle update shader. - Add
force_fieldexample showcasing a repulsor, an attractor and the conforming to sphere functionality.
- Add
- Add rendering with a 2D camera.
- Renamed the
ToWgslFloattrait 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>::ratiofield private to avoid any modification viaGradient<T>::keys_mut()which would corrupt the internal sorting of keys. - Fixed a bug where adding the
HanabiPluginto an app without spawning anyParticleEffectwould 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.