Table of Contents
A particle is a textured rectangle or a small mesh that can be drawn quickly and simply, usually with an additive blend mode. They are mostly used to create interesting graphical effects.
Particles are managed within particle system objects, which keep track of a number of particles with similar properties, and an identical function pipeline to describe the transition of its particles' properties.
The particle system is implemented in C++ and is accessible from both the scripting environment and C++. The Python module for particle systems is named Pixie.
Each particle system is presently responsible for a set of particles of the same texture. While a particle system can change its particle texture colour dynamically, effects that require different simultaneous textures require separate particle systems.
A particle system is a conglomerate of the following:
-
Particles. This is a container that has specific allocation requirements determined by the renderer type. Currently there are contiguous particles, which have optimal insert/erase characteristics; and FixedIndex particles, these behave like a circular buffer and ensure that particle indices remain constant over their lifetime. FixedIndexParticles are a requirement for trail rendering and mesh particle rendering.
-
Particle Actions, which are responsible for the movement of particles (these actions do all the work of moving, creating and destroying each particle).
-
Particle Renderer, which is in charge of drawing the particles.
The Particle System itself provides a common access point for all three objects.
The Particle Renderer is for the most part hidden within the particle system. Likewise, the particles themselves are inaccessible to the outside. Most game code associated with the Particle System involves the creation of special Particle Actions.
Particle Actions work only during the update phase of the client. They are called in turn during the tick method for a particle system, and the combined actions of each produce the overall particle effect of the system.
For each particle system class, there is a corresponding python wrapper class. For example, MetaParticleSystem is wrapped by PyMetaParticleSystem. These python wrappers hold no state, they only hold a smart pointer reference to the underlying C++ object. The reason for this separation between the C++ object and their python equivalent is to allow the full construction of particle systems in the background thread (Python objects may only be constructed in the main thread.)
There is one more particle system class, which is the ChunkParticles class. This is entirely C++ and implements a ChunkItem for particle systems, it allows particle systems to be placed in the world via the World Editor tool. Note that such particle systems should be automatically time triggered, as there is no way to access them at run-time in python and turn them on or off.
At present, the procedure for using a particle system involves the following steps:
-
Particle System is created using Particle Editor.
-
Particle Editor is used to create the different types of Particle Actions that control the particles.
-
Any texture and material effects are set.
-
Particle System is added to a model, in order to be displayed and updated.
Particle Systems can easily be created in the BigWorld
Particle Editor and saved as XML files — typically under folder the
resource tree <res>
.
In the client, they can either be placed directly into the scene using the
World Editor, or invoked from Python as such:
import Pixie ps = Pixie.create( "my_particle_system.xml" ) BigWorld.player().model.node( "biped head" ).attach( ps )
Note that particle systems can take a reasonably long time to construct - for a moderately complex system it might take 5ms to parse the xml file, create all the subsystems and hook them all up together. For this reason, it is highly recommended that the script-writer load Particle Systems asynchronously, using either the Pixie.createBG method, the BigWorld.loadResourceListBG method, or using Entity prerequisites. For example:
Example 1: import Pixie ps = Pixie.createBG( "my_particle_system.xml", self.onLoadPS ) def onLoadPS( self, ps ): BigWorld.player().model.node( "biped head" ).attach( ps ) Example 2: import BigWorld BigWorld.loadResourceListBG( ("particles/one.xml", "particles/two.xml"), self.onLoadResources ) def onLoadResources( self, resources ): self.model.root.attach( resources["particles/one.xml"] ) self.model.root.attach( resources["particles/two.xml"] )
There are four main categories of Particle Actions:
-
Source
Creates particles
-
Movement
Changes the velocity or position of the particles based on a set of rules. One movement action is special as it applies the effect of velocity onto the position of each particle.
-
Sink
Removes particles from the list of active particles according to a set of rules.
-
Alteration
Changes the appearance of the particles.
Source actions can create particles over regular periods of time, on demand, or even be sensitive to the movement of the model to which it is attached. The size, colour, velocity, position, and even age of the particles can be specified upon creation. The key behind vector descriptions of particles is the Vector Generation subsystem.
Each source action requires three vector generators. A vector generator is a class of objects that randomly create vectors within a given space; the space is usually defined by the type of generator and the parameters given to it. For example, a sphere generator may accept a point in 3D space and a radius value, while a line generator may accept two points in 3D space.
The three generators are used to find the initial position, velocity, and colour of the particle when created. When calculating position and velocity, the local space of the model is taken into account, but only for the creation of the particle. What this means is that a cube described by two points, (-0.5, -0.5, -0.5) to (0.5, 0.5, 0.5) is roughly the bounding box of the particle in position.
Movement actions act on every particle in the system. There is a variety of movements that can be applied to a particle. Examples would be force, stream, barrier, and jitter.
Basic movement of particles due to velocity is calculated automatically within the particle system, taking wind also into account.
Particles can also undergo full scene collision — e.g., spent bullets tumbling down steps.
Sink actions remove particles from the system, whether it is due to age or for moving beyond a specified boundary. Examples of sink actions are sink, barrier, and splat.
It is important to note that without sink actions, particles once created will never leave the system, eventually forcing it to hit its particle limit.
Alteration actions affect the appearance of the particles. They can modify the shading, alpha level, and size of the particle over time. They are typically used in smoke effects, gentle fading in and out, and glowing effects. Particle textures can be animated if the texture specified is an animated texture.
The particle system renderer is separate from the main particle system calculations, and allows not just texture-based particles, but also sprite-based and mesh-based particles. Multiple particle systems can share the same renderer.
The mesh particle renderer can use any visual, but for optimum performance, meshes specific for use in particle systems should be exported from 3ds Max or Maya in groups of 15, having the Mesh Particles option button selected in its respective exporter dialog. For more details, see the document Content Tools Reference Guide's chapter 3ds Max and Maya Exporters .

3ds Max Visual Exporter dialog and Maya Visual Exporter dialog
Note
For details on any class mentioned in this section, see the Client Python API's entry Class List.
To have a particle system follow a bone and orient to an external target, you should attach it to a node, and then point that node using a tracker.
The tracker class provided by the BigWorld module (BigWorld.Tracker) can be set up using a variety of DirectionProviders to point a node in the appropriate direction:
-
EntityDirProvider
Points a node in the direction that an entity is facing.
-
DiffDirProvider
Points a node towards the position given by a MatrixProvider (e.g., to point at the head node of another entity, or to point in a constant world direction).
-
ScanDirProvider
Makes a node swing back and forth around its Y-axis (e.g., to simulate a security camera).
Depending on your particular game, you might not be able to use a tracker to point a node on the source model. For example, the node may be part of a character's skeleton and have some mesh rigged to it, so you may not want to actually point to this bone (only the particle system attached to it). If this is the case, then you will need to create a new node on which to mount your particle system — ask your artists to build dummy nodes into their models on which to mount FX.
To have a particle system exist at a position that is not provided by a bone, you should attach it to a separate model, and display the separate model using Entity.addModel. Using Entity.addModel instead of Entity.model.node(xxx).attach means that the model exists at location independent from the entity — in fact, it means that nobody will be setting the position/direction of the model, and it is entirely up to the script.
Once you have an independent model, you can set its position and orientation to any MatrixProvider, by using the Servo Motor. As explained in the Client Python API, the Servo class is a Motor that sets the transform of a model to the given MatrixProvider — as the MatrixProvider is updated, the model moves. The signal attribute is the MatrixProvider that sets the model's transform.
This way, you can use the Servo Motor to move the model to a position provided by any dynamic matrix. If the independent model has a node to point to, then you can set its orientation by using a Tracker, as explained above. This allows you to detach the position of a particle system from a bone, but still set the orientation of the particle system to the orientation of the bone, or towards an external target.
Finally, if you want a particle system to exist at a fixed point/orientation, then use the ParticleSystem's explicitPosition and explicitDirection attributes. These set the position/orientation of a particle system once, and cannot be set to dynamic matrices. Note that if you use either attribute, you will need to use both (setting either attribute tells the particle system that it is using an 'explicit transform' derived from both attributes).
Note
Be careful when attaching particle systems to bones! To attach a particle system to a bone, a PyModelNode must be retrieved. If there are no existing PyModelNodes referencing the bone you want to use, then its transform is undefined until the next draw call. Since python scripts are updated *before* the draw call, there is no way of knowing where a node is at the time of first retrieval. Therefore in this case, you should only attach a particle system to a PyModelNode when you know it has been updated. Your possible options are :
-
Retrieve and keep a reference to the bone when you create your model. This will ensure at a later date when a particle system is attached to that bone, that the bone's position is well-known.
-
Retrieve a reference to the bone you require, and callback yourself the next frame to attach the particle system to it. For example :
BigWorld.callback( 0.0, partial( self.model.node("HP particles").attach, self.particles ) )