APEX EffectPackage Programmers Guide

../_images/SimpleTurbulence.png

Introduction

APEX Effect Package assets are authored using the ParticleEffectTool (PET). An EffectPackage represents a collection of related particle asset/actors whcih can be instantiated as a single actor and manipulated in real time. A single EffectPackage might contain a number of Emitters, a turbulence grid, a heat source, and some field samplers. Each of these effects can have different timing characteristics, such as a duration and loop counters. Each effect can have a parent relative transform so that when the root actor moves all children actors are revised accordingly.

Loading the EffectPackage databases

If you use the ‘publish’ feature of the ParticleEffectTool you have the option of pre-loading all of the effects which have been authored when your application starts up. This is an optional feature. You can still just load invididual asset files on demand like you would do normally. However, in the case of effects it can easily explode into hundreds of individual files that need to be managed. As a matter of convenience you can use the ‘publish’ feature of the ParticleEffectTool to produce a set of binary serialized data assets which contain all assets that were authored with the tool. Since these assets are largely just composed of a small collection of numeric values they are quite small and would not generally be considered a problem to preload on application startup.

The series of binary serialized files that are produced by the ParticleEffectTool are as follows:

PET Published Binary Output Asset Files

PUBLISH FILE NAME Purpose
EffectPackageEffectPackagesDatabase.apb Contains an array of all EffectPackage assets authored in PET
EffectPackageEmittersDatabase.apb Contains an array of all Emitter assets authored in PET
EffectPackageFieldSamplersDatabase.apb Contains an array of all FieldSampler related assets authored in PET
EffectPackageGraphicsEffectsDatabase.apb Contains an arary of all IOFX assets authored in PET
EffectPackageGraphicsMaterialsDatabase.apb Contains an array of all Graphics Material properties authored in PET; not required
EffectPackageParticleSimulationsDatabase.apb Contains an array of all BasicIOS and ParticleIOS assets authored in PET

The following code snippet shows how one of these APB files can be loaded into memory:

// This method will load a binary serialized file on disk with the name specified and deserialize it into memory.
NvParameterized::Interface      *loadParams(const char *name)
{
        NvParameterized::Interface *ret = NULL; // The default return value.

        std::string fname = mMediaPath + std::string(name); // Produce the fully qualified path name for the asset.
        FILE *fph = fopen(fname.c_str(), "rb"); // Open the file for read access.
        PX_ASSERT(fph); // Assert if the file open fails; this should always succeed in this sample./
        if ( fph )
        {
                fseek(fph,0L,SEEK_END);
                size_t len = ftell(fph);        // Find out the length of the file.
                fseek(fph,0L,SEEK_SET);
                void *mem = ::malloc(len);      // Allocate memory to hold the file
                PX_ASSERT(mem); // Assert that the memory allocation succeeded.
                if ( mem )
                {
                        size_t r = fread(mem,len,1,fph);        // Read the entire file into memory in a single operation.
                        PX_ASSERT(r==1);        // Assert that the file read succeeded.
                        if ( r == 1 )
                        {
                                // Create a binary deserializer using the APEX SDK
                                NvParameterized::Serializer *ser = mApexSDK->createSerializer(NvParameterized::Serializer::NST_BINARY);
                                if ( ser )
                                {
                                        // Create a memory read stream
                                        physx::PxFileBuf *fb = mApexSDK->createMemoryReadStream(mem,(physx::PxU32)len);
                                        if ( fb )
                                        {
                                                // Deserialize this data.
                                                NvParameterized::Serializer::DeserializedData desData;
                                                NvParameterized::Serializer::ErrorType err = ser->deserialize(*fb,desData);
                                                if ( err == NvParameterized::Serializer::ERROR_NONE )
                                                {
                                                        if ( desData.size() == 1 )
                                                        {
                                                                NvParameterized::Interface *params = desData[0];
                                                                if ( params )
                                                                {
                                                                        ret = params;   // Return the NvParameterized::Interface representing the binary deserialized data.
                                                                }
                                                        }
                                                        else
                                                        {
                                                                PX_ALWAYS_ASSERT();
                                                        }
                                                }
                                                else
                                                {
                                                        PX_ALWAYS_ASSERT();
                                                }
                                                mApexSDK->releaseMemoryReadStream(*fb); // Release the read stream
                                        }
                                        ser->release(); // Release the serializer
                                }
                        }
                        ::free(mem); // Free the temporary memory allocated to hold the source file.
                }
                fclose(fph);    // Close the file
        }

        return ret;     // Return the NvParameterized data
}

Finally, you would register these assets with the particles module as follows:

// This code section loads the published particle related assets authored using the ParticleEffectTool and registers them with the Particles module

// Load the EffectPackage database
iface = loadParams("EffectPackageEffectPackagesDatabase.apb");
PX_ASSERT(iface);
if ( iface )
{
        mModuleParticles->setEffectPackageDatabase(iface);
        iface->destroy();
}

// Load the Emitter database
iface = loadParams("EffectPackageEmittersDatabase.apb");
PX_ASSERT(iface);
if ( iface )
{
        mModuleParticles->setEffectPackageEmitterDatabase(iface);
        iface->destroy();
}
// Load the FieldSampler database
iface = loadParams("EffectPackageFieldSamplersDatabase.apb");
PX_ASSERT(iface);
if ( iface )
{
        mModuleParticles->setEffectPackageForceFieldDatabase(iface);
        iface->destroy();
}
// Load the GraphicsEffects database (IOFX)
iface = loadParams("EffectPackageGraphicsEffectsDatabase.apb");
PX_ASSERT(iface);
if ( iface )
{
        mModuleParticles->setEffectPackageIOFXDatabase(iface);
        iface->destroy();
}

// Load the GraphicsMaterials database
iface = loadParams("EffectPackageGraphicsMaterialsDatabase.apb");
PX_ASSERT(iface);
if ( iface )
{
        mModuleParticles->setEffectPackageGraphicsMaterialsDatabase(iface);
        iface->destroy();
}

// Load the Particle Simulations (IOS) database
iface = loadParams("EffectPackageParticleSimulationsDatabase.apb");
PX_ASSERT(iface);
if ( iface )
{
        mModuleParticles->setEffectPackageIOSDatabase(iface);
        iface->destroy();
}

Creating an Asset based on the pre-registered data in the Particles Module

In the named resource provider callback, you can satisfy request for particle related assets which were predfined in the Particles module using the following snippet of code:

if ( mModuleParticles ) // If we are using the Particles module
{
        // See if this data for this resource was preloaded into the particles module.
        NvParameterized::Interface *iface = mModuleParticles->locateResource(pname,nameSpace);
        if ( iface )
        {
                NvParameterized::Interface *copyInterface;
                iface->clone(copyInterface);    // Create a copy of the parameterized data.
                PX_ASSERT(copyInterface);
                if ( copyInterface )
                {
                        nvidia::apex::Asset *asset = m_apexSDK->createAsset(copyInterface,pname);       // Create the asset using this NvParameterized::Inteface data
                        PX_ASSERT(asset);
                        resource = asset;       // return this resource that we just created
                        if ( asset == NULL )
                        {
                                // If it failed to create the asset, destroy the interface data.
                                copyInterface->destroy();
                        }
                }
        }
}

Creating an EffectPackage Actor

The following code snippet demonstrates how to create an instance of an EffectPackageActor by name:

virtual nvidia::apex::EffectPackageActor *createEffectPackage(const char *effectPackageName,   // The name of the effect package to create
                                                          const physx::PxTransform &p,         // The root transform of the effect package actor
                                                          nvidia::apex::Scene *scene) // The APEX scene to create the effect package actor into
{
       nvidia::apex::EffectPackageActor *ret = NULL;   // Default return value is NULL
       // Use the Named Resource Provider to find the corresponding EffectPackage asset
       nvidia::apex::Asset *effectPackageAsset = (nvidia::apex::Asset *)mApexSDK->getNamedResourceProvider()->getResource(PARTICLES_EFFECT_PACKAGE_AUTHORING_TYPE_NAME,effectPackageName);
       if ( effectPackageAsset && scene )      // If the asset was found and we have a valid scene.
       {
               NvParameterized::Interface *defaultActorDesc=effectPackageAsset->getDefaultActorDesc();
               if ( defaultActorDesc )
               {
                       NvParameterized::setParamTransform(*defaultActorDesc,"InitialPose",p);
                       nvidia::apex::Actor *effectPackageActor = effectPackageAsset->createApexActor(*defaultActorDesc,*scene);
                       ret = static_cast< nvidia::apex::EffectPackageActor *>(effectPackageActor);
               }
       }
       return ret;
}

Modifying an EffectPackageActor : EffectPackageActor.h

Once you have created an EffectPackageActor you can modify its behavior in the following ways:

/**
\brief Fade out the EffectPackageActor over a period of time.
This method will cause the actors within the EffectPackage to be 'faded out' over a period of time.
The EffectPackageAsset will define how this fadeOut operation is to be applied.  Some actors may
simply be turned off over this duration while others are modulated in some way.  For example, by default,
emitter actors will have their emitter rate reduced over this duration so they do not appear to
immediately cut off

\param [in] fadeTime : The duration to fade out the associated effects in seconds
*/
virtual void fadeOut(physx::PxF32 fadetime) = 0;

/**
\brief Fade in the EffectPackageActor over a period of time.
This method will cause the associated effects to be faded in over a period of time.
It will begin the linear interpolation fade process relative to the current fade time.
Meaning if you had previously specified a fadeOut duration of 1 second but then execute
a fadeIn just a 1/2 second later, will will begin fading in from a 50% level; not zero.

\param [in] fadeTime : The duration to fade this effect in, in seconds
*/
virtual void fadeIn(physx::PxF32 fadetime) = 0;

/**
\brief Returns the number of effects within this effect package.
*/
virtual PxU32 getEffectCount(void) const = 0;

/**
\brief Returns the type of effect at this index location

\param [in] effectIndex : The effect number to refer to; must be less than the result of getEffectCount
*/
virtual EffectType getEffectType(PxU32 effectIndex) const = 0;

/**
\brief Returns the base Actor pointer for this effect.
Use this method if you need direct access to the base Actor pointer relative to this
effect.  You must use 'getEffectType' to know what type of actor this is referring to before
you cast it to either an emitter, or heat source, or turblence actor, etc.
It is important to note that the application cannot cache this pointer as it may be deleted in
subsequent frames due to level of detail calculations or timing values such as fade out operations.

\param [in] effectIndex : The effect number to refer to; must be less than the result of getEffectCount

\return Returns the Actor pointer for this effect.  This may be NULL if that effect is not currently active.
*/
virtual Actor *getEffectActor(PxU32 effectIndex) const  = 0;

/**
\brief Forces the state of all emitters associated with this effect package to be on or off.
This method allows the application to manually force all emitters to be active or inactive
based on an explicit call.

\param [state] : Determines whether to set all emitters on or off
*/
virtual void setEmitterState(bool state) = 0;

/**
\brief A helper method to return the active number of particles being simulated by all related effects in this EffectPackageActor

\return Returns the number of particles active relative to this EffectPackageActor
*/
virtual PxU32 getActiveParticleCount(void) const = 0;

/**
\brief This helper method reports if any emitters within this EffectPackageActor are still actively emitting particles.

This method allows the application to determine whether or not any emitters are actively running
relative to this actor.  Typically this would be used to defer deleting an effect while it is still active.

\return Returns true if any emitters in the actor are still active.
*/
virtual bool isStillEmitting(void) const = 0;

/**
\brief Allows the application to manually enable this EffectPackageActor

This method is used by the application to manually force the EffectPackageActor to be enabled.
This is generally used based on some kind of game logic where the EffectPackageActor has been
pre-allocated and placed into the world but is initially inactive until some event occurs.

\param [state] Set the current enabled state to true or false
*/
virtual void setEnabled(bool state) = 0;

/**
\brief Returns the current enabled state for this actor
*/
virtual bool getEnabled(void) const = 0;

/**
\brief Sets the root pose for this EffectPackageActor
It's important to note that child-relative poses for each sub-component in an EffectPackageActor
are authored in the ParticleEffectTool.

\param [pose] : Sets the current root pose for this EffectPackageActor
*/
virtual void setPose(const physx::PxTransform &pose) = 0;

/**
\brief Returns the current root pose for this EffectPackageActor
*/
virtual const PxTransform& getPose(void) const = 0;

/**
\brief This method is used by the ParticleEffectTool to refresh a currently running effect with real-time property changes
*/
virtual void refresh(void) = 0;

/**
\brief Releases this EffectPackageActor and all associated children effects
*/
virtual void release(void) = 0;

/**
\brief A convenient helper method to return the name of the asset used to to create this actor.
*/
virtual const char *getName(void) const = 0;