Features Used in Particle Calculations

Animation Frame Control

ParticleModel and ParticleEmitter objects both have nw::anim::AnimFrameController objects.
Call the ParticleAnimFrameController function to get these AnimFrameController objects.

Time can be advanced by making a step setting (SetStepFrame) or updating the frame (UpdateFrame). Advance to a particular frame by applying an offset from the previous frame to the animation. Depending on the animation type, values may differ when advancing one frame at a time.

In addition, the emitter emits for the amount of time that has advanced since an update was last called. A large volume of particles is therefore emitted if many frames are suddenly advanced at once.

This is not supported for reverse playback (where frame numbers are decreased).

Deleting Particles

Use the following methods to delete generated particles.
nw::gfx::ParticleCollection::KillParticle: Delete a specific particle.
nw::gfx::ParticleCollection::KillParticles: Kill all particles.
If the deleted particle specifies child particle emission, then those child particles are emitted at the time of deletion.

Example:
nw::gfx::ParticleSet* particleSet;

// Get the ParticleCollection.
particleCollection = particleSet->GetParticleCollection();

// Delete a specific particle (in this case, the fifth).
{
    // Get an index to the active particles.
    u16* activeIndex = (u16*)particleCollection->GetStreamPtr(
        nw::gfx::PARTICLEUSAGE_ACTIVEINDEX,nw::gfx::PARTICLE_BUFFER_FRONT);

    // Get the index.
    s32 index = activeIndex[5];

    // Delete the particle at the specified index.
    particleCollection->KillParticles(index);
}

// Delete all generated particles.
{
    particleCollection->KillParticles();
}

Changing Resource Parameters

To apply an effect to all nodes created from the same resource, you can change them by obtaining the resource and directly accessing associated parameters.

To apply an effect to only one node out of all nodes created from the same resource, copy the resource for that node and manipulate the copied resource.
Resources can be copied automatically at time of node creation. If the IsResourceCopyEnable flag inside a resource is set before node creation, that resource is copied and stored at the time of node creation.
If the copied resource is for a given created node only and that node is destroyed, the copied resource is also destroyed. Consequently, be sure to pay attention to how long the node has been in existence when using a copied resource outside a node. In addition, since the original resource is also accessed even if it has been copied, be sure to manage resources as usual for the original resource.
Flags can be set ahead of time in the CreativeStudio UI. Set the flags for the following resources appropriately when making changes after loading resources with an application.
Resources that include IsResourceCopyEnabled

APIs for getting copied resources

Specify true as an argument to these API functions to get resources that are only valid when being copied for use by a node. If there are only shared resources, IsValid returns false. The default argument value for these functions is false. Specify false to prioritize nodes when getting resources, but if there are none, the function returns the shared resources.

Use the GetInitializers and GetUpdaters functions of the ParticleSet class to get a list of management information for initializers or updaters.

Notes on Changing Emission Volume, Lifespan, Etc.

Pay attention to possible memory shortages with ParticleCollection when making changes to parameters (such as the emission volume, emission interval, and lifespan) that will change the number of particles that exist simultaneously. You cannot change memory capacity after nodes are created. Be sure to make any changes to values in ResParticleCollection before creating nodes.

In addition to changing memory capacity at run-time, you can also allocate the maximum number of bytes anticipated ahead of time using CreativeStudio.

If there is a memory shortage, some particles that should have been emitted won't be. Once a particle has failed to be emitted it will no longer be generated at all, even if a spot opens for it due to another particle disappearing. A particle generated after the spot opens will be emitted. This can make it look like particles are being emitted intermittently.

Changing Particle Information

Since most particle information is passed directly to the GPU shader, it's hard to manage a program built to reflect the internal implementation. An application must have some knowledge of the internal structure to change this information. Here's the explanation for this.

Included in particle information are streams that can include different values for each particle and fixed-value parameters in a ParticleSet.

Whether particle information is a parameter or a stream is determined upon optimization of the binary output from CreativeStudio.

Conditions for Optimizing Streams to Parameters

If all of the following conditions are satisfied, the Initializer is deleted and the stream is also output as parameters (fixed-value).

In addition to the distinction between parameters and streams, there is also a distinction between information that is used only in calculations by the CPU and information (VBO and vertex parameters) that is directly handled by the shader. This information is divided up at run-time depending on the attribute type. A double buffer is used for vertex attributes. A single buffer is used for other information.

Handling Attribute Types at Runtime

Type Role Data Type Parameter Availability Is Vertex Attribute?
TRANSLATE Location (local coordinate system) VEC3 Stream only Vertex attribute
SCALE Scale (local coordinate system) VEC3 Stream/Parameter Vertex attribute
ROTATE Rotation (local coordinate system) VEC3 Stream/Parameter Vertex attribute
COLOR RGB Color VEC3 Stream/Parameter Vertex attribute
ALPHA Alpha f32 Stream/Parameter Vertex attribute
TEXTURETRANSLATE0 Texture coordinate translation. VEC2 Stream/Parameter Vertex attribute
TEXTURESCALE0 Texture coordinate scale. VEC2 Stream/Parameter Vertex attribute
TEXTUREROTATE0 Texture coordinate rotation. f32 Stream/Parameter Vertex attribute
SCALE_EXT Scale (extended) VEC3 Stream/Parameter Vertex attribute
BIRTH Date created. ParticleTime Stream only -
LIFE Lifetime ParticleTime Stream/Parameter -
VELOCITY Speed VEC3 Stream only -
ACTIVEINDEX Indirect reference index to the element in use. u16 Stream only Vertex attribute
FREEINDEX Indirect reference index to a usable element. u16 Stream only -
NGE_TIMELIMIT Negative number (for speedup purposes) indicates the time when the particle should disappear. ParticleTime Stream only -

 

Changing Values

There are four methods for changing values. The one to use depends on whether information is a stream or a parameter and whether it's a vertex attribute or other information.

For a sample implementation, see ParticleChangeVtxDemo.

Changing Parameters

Overwrite parameters using ParticleCollection::SetParameter. For LIFE, you can also overwrite using ParticleCollection::SetLifeParameter (Recommended).

For non-vertex parameters (LIFE only), you can only rewrite the content of memory pointed to by the parameter pointer obtained by ParticleCollection::GetParameterPtr. Because a single-buffer is used, both PARTICLE_BUFFER_FRONT and PARTICLE_BUFFER_BACK have the same content.

For vertex attribute parameters, you can also rewrite any commands that have already been created, in addition to the content of memory pointed to by the parameter pointer obtained by ParticleCollection::GetParameterPtr. Because commands are double-buffered, only one the buffers can be rewritten per call. Note that a command that does not specify a buffer does not rewrite anything. There may be some difference between the contents of the address obtained using ParticleCollection::GetParameterPtr and the command double-buffer status. Although you can specify PARTICLE_BUFFER_FRONTPARTICLE_BUFFER_BACK, do not change PARTICLE_BUFFER_BACK at any time a command may be issued to the GPU. Essentially, you should not write to PARTICLE_BUFFER_BACK at all because this timing is difficult to ascertain.

Moreover, parameters are never changed by the library after commands are created.

Changing Streams

Use ParticleCollection::GetStreamPtr to get streams. If the function returns NULL, either the arguments are wrong or the specified class is being used as a parameter.

In general, get the stream from PARTICLE_BUFFER_FRONT.

The obtained stream is an array of types determined based on the class. Use the ParticleCollection::GetStreamPtr function to get the maximum number of entries that can be stored in the array. This stream is not necessarily used padded from the start. You must use an index that can be obtained by the PARTICLEUSAGE_ACTIVEINDEX class.

See ParticleUpdaterDemo for a sample implementation of making changes using UserUpdater.

Getting ACTIVEINDEX

This section describes how to get ACTIVEINDEX, which is required to access streams.

ACTIVEINDEX is used as the vertex index of DrawElements as a VBO. Applications cannot rewrite this value because it affects various internal elements.

Although ACTIVEINDEX is usually stored starting from the earliest in the order created, if the rendering order is reversed (ResParticleShapeBuilder::IsAscendingOrder), it is stored starting from the oldest. Use ParticleCollection::GetCount() to get the number of valid instances.

Basically, get streams from PARTICLE_BUFFER_FRONT, but get them from PARTICLE_BUFFER_BACK when getting PARTICLEUSAGE_ACTIVEINDEX inside UserUpdater. Even then, be sure to get all information other than ACTIVEINDEX from FRONT.

This requirement is due to the following process flow.

  1. Create particles (AddParticles/InitializeParticles).
  2. Update (UpdateParticles).
    1. Swap FRONT and BACK by swapping buffers.
    2. Copy the VBO stream other than ACTIVEINDEX from BACK to FRONT.
    3. Update (UserUpdater is called here).
    4. When deleting particles that have finished their lifespans, delete unnecessary particles from BACK and arrange in FRONT.

You must get the ACTIVEINDEX used by UserUpdater from the BACK because the deletion process overlaps stream copying.
Do not overwrite content because rendering for BACK may be in progress.

Particle Memory Usage

This section describes the memory used per particle as of NintendoWare 1.0.1.

Values are handled as floating point numbers because particles are drawn by passing memory that stores particle information directly to the GPU. (This is done because quantizing multiple attributes increases the processing load on the GPU.)

Type Role Size Omissibility Number of Buffers
TRANSLATE Location (local coordinate system) 12 bytes × 2
SCALE Scale (local coordinate system) 12 bytes 2
ROTATE Rotation (local coordinate system) 12 bytes 2
COLOR RGB Color 12 bytes 2
ALPHA Alpha 4 bytes 2
TEXTURETRANSLATE0 Texture coordinate translation. 8 bytes 2
TEXTURESCALE0 Texture coordinate scale. 8 bytes 2
TEXTUREROTATE0 Texture coordinate rotation. 12 bytes 2
SCALE_EXT Scale (extended) 12 bytes 2
BIRTH Date created. 4 bytes × 1
LIFE Lifetime 4 bytes 1
VELOCITY Speed 12 bytes × 1
ACTIVEINDEX Indirect reference index to the element in use. 2 bytes × 2
FREEINDEX Indirect reference index to a usable element. 2 bytes × 1
NGE_TIMELIMIT Negative number (for speedup purposes) indicates the time when the particle should disappear. 4 bytes × 1

50 bytes if all omissible items can be omitted. 210 bytes if they could not all be omitted.

The number of bytes specified for "work memory at runtime" under CreativeStudio is allocated per ParticleSet.

Memory used by main classes (main base classes are given in parentheses).

sizeof (values in parentheses are for version 1.1)
ParticleModel 636 (564)
ParticleSet 144 (136)
ParticleCollection 444 (436)
ParticleShape 528 (516)
ParticleEmitter 400 (388)
(SceneObject) 12 (12)
(SceneNode) 80 (72)
(TransformNode) 336 (324)
(Model) 556 (508)

In addition, memory for pointers such as to materials, the command cache, and children is also allocated.

1296 bytes per ParticleShape is allocated for the command cache to be used for particles.

Processing When Using Hierarchical Structures

When a particle is generated, its position is determined by the emitter. However, the coordinates are converted so that its position does not shift when passed to the particle model. In other words, regardless of how the hierarchy is arranged, emitted particles appear in the emitter position at this point.

After emission, particles are drawn using the model coordinate system.

As an exceptional option for hierarchical structures, particle sets include an option for forcing the use of the world coordinate system. If this option is specified, the world coordinate system is used for the particle set regardless of the hierarchical structure. In other words, the status is the same as if there were no hierarchical structure.

User Updater

An updater is a processing unit used to animate particles for a ParticleSet. The library includes a means of adding user-defined updaters. Use this to provide an application with features not provided by the library.
ParticleUpdaterDemo is a sample demo of a user-defined updater.

Preparing Resources

The library supports the ResParticleUserUpdater resource for user updaters. A user process is used as the callback when a ParticleSet processes this resource.

ResParticleUserUpdater does not currently support intermediate files created using CreativeStudio. To add a user-defined updater, add the following to the ParticleUpdaters list in the intermediate file:
<ParticleUserUpdaterXml IsUpdaterEnabled="true" UserParameter="0">
<ParticleAnimationData />
<TargetStreams />
</ParticleUserUpdaterXml>
Although CreativeStudio does not support the addition and deletion of user updaters, it does support intermediate file reading and writing, binary output, and content viewing. ParticleUserUpdater must be set for each ParticleSet. The ProcessPriority value determines the updater process priority. Specify 0 to have the user updater run before all other updaters. Conversely, specify 1000 to run the user updater last.

Using User-defined Updaters with Applications

Search for updaters supported by ResParticleUserUpdater from among updaters for the created ParticleSet, and set a user-defined function pointer for updater.work.

For details, see how the SetUserUpdater function is used in ParticleUpdaterDemo.

Emission Volume from an Emitter

This section gives an overview of parameters related to emitter emission and what they mean.

Emission Timing

First, the basic concept is summed up as follows:

This is the fundamental concept.

This timing provides an opportunity at the integral frame level to control emission based on whether the actual time has exceeded this timing.
This is evaluated during actual calculations. Particles emitted between the last evaluation and the next are all treated as if generated at time of evaluation. For example, consider a particle set to be emitted each frame at a playback rate of 2.0. In this case, double emission will take place once every two frames. There is no need to consider the time difference of unevaluated frames.

There is no opportunity for emission with an emission period of 0. With an emission period of 1, there is only one chance for generation (the first frame).

The emitter stores the timing for the next emission and updates information during emission processing.
Timing for the next emission is given by the following formula.

(int)(emission_interval * (1.0f + emission_interval_random * random_number_between(-1.0f, +1.0f))

This is calculated to find the time of next emission. However, 1 is used if the calculated value is less than 1. This means there is no emission until the next frame even if a random number is included.

Emission Volume

The following equation is calculated at each time of emission.

emission_volume * (1.0f + emission_volume_random * random_number_between(-1.0f, +1.0f))

The integer part of the accumulated value is the number of particles to be emitted at time of evaluation.

As an exception, an emission volume less than 1 is handled as 1 at time of first evaluation if the emitter's emission volume exceeds 0. This is due to the fact that, even though it may take a long time to accumulate a value of 1 when given emission volume values less than 1, there are many cases where you want to emit exactly one particle in the first frame.
If this operation is a problem, either make it so that the emitter is not evaluated until actual time of emission or control things so the emission volume is 0.

Generating Particles in the Middle of a Scene

There could be cases where you want to emit particles partway through a scene. For instance, when creating rain particles, you might want to generate particles that skip the beginning of the rain. Leave just the frames you want to skip as empty of particles to create this effect, thereby skipping the beginning of the rain and begin generating from the midst of a rain shower.

Leave Just Specified Frames Empty

Work with both the ParticleModel and the ParticleEmitter objects to leave certain frames empty. The example below leaves 100 frames empty of particles.

for (u32 k = 0; k < 100; ++k)
{
    for (u32 i = 0; i > modelNodeArray.size(); ++i)
    {
        modelNodeArray[i]->ParticleAnimFrameController().SetStepFrame(1.0f);
        modelNodeArray[i]->UpdateParticleFrame();
    }

    for (u32 i = 0; i > emitterNodeArray.size(); ++i)
    {
        emitterNodeArray[i]->ParticleAnimFrameController().SetStepFrame(1.0f);
        emitterNodeArray[i]->UpdateParticleFrame();
        emitterNodeArray[i]->Emission(s_ParticleContext);
    }

    for (u32 i = 0; i > modelNodeArray.size(); ++i)
    {
        for (u32 j = 0; j > modelNodeArray[i]->GetParticleSetsCount(); ++j)
        {
            modelNodeArray[i]->GetParticleSets(j)->UpdateParticles(s_ParticleContext);
        }
    }
}

Precautions for No-Particle Frames

Keep the following points in mind when leaving frames empty of particles.


CONFIDENTIAL