particle (lx_particle.hpp)

From The Foundry MODO SDK wiki
Jump to: navigation, search
There are security restrictions on this page


Contents

Particle Source

Any item can potentially act as a particle source. This interface allocates a particle object for a given item.

Prepare
Given an ILxEvaluation interface, the item selects that channels it needs for input. It can return an index, generally for the first channel it added.
Evaluate
After prepping, this method is called to allocate the particle source object. The ILxAttributes interface and index are used to read channels selected by the Prepare() method.

The particle object doesn't have a specialized interface, and is instead implemented using a TableauSurface interface to get the point data, and an attributes interface to set the special attributes.

(1) SDK: ILxParticleItem interface
         LXxMETHOD( LxResult,
 Prepare) (
         LXtObjectID              self,
         LXtObjectID              eval,
         unsigned                *index);
 
         LXxMETHOD( LxResult,
 Evaluate) (
         LXtObjectID              self,
         LXtObjectID              attr,
         unsigned                 index,
         void                   **ppvObj);

(2) SDK: Declarations
 #define LXu_PARTICLEITEM        "BA13DD5D-5093-4D0D-BEFE-119AD4F1FACB"

(3) User Class: ParticleItem method
         bool
 Alloc (
         ILxUnknownID             attr,
         unsigned                 index,
         CLxLocalizedObject      &loc)
 {
         LXtObjectID              obj;
 
         if (LXx_OK (Evaluate (attr, index, &obj)))
                 return loc.take (obj);
 
         loc.clear ();
         return false;
 }

Empty ParticleItem Python user class.

(4) PY: ParticleItem method
 pass

Any item can read particle sources by setting this tag. This allows particle graph connections for the item.

(5) SDK: Declarations
 #define LXsPKG_READPARTICLES            "readsParticles"
 #define LXsITYPE_SINGLEPARTICLE         "1"
 #define LXsITYPE_MULTIPARTICLE          "*"
 #define LXsITYPE_ORDEREDPARTICLE        "o"

The particle object can also support an ILxAttributes interface to provide options that the client can set.

SEED
integer attribute that is the random seed for the particle ID or other random attributes of the paricle source.
USEPOL
integer setting that can be set on mesh particle sources. 0 generates particles on all vertices; 1 generates particles at polygon centers rather than on points; and 2 generates particles only at detached vertices.
UPAXIS
integer channel which defines the up axis as 0, 1 or 2. This is used for point clouds to determine the best way to orient the particles.
DEFORM
integer boolean that can be set on mesh particle sources. When true particles are on the deformed mesh, on the base mesh if not.
SPACING
spacing is used to increase the number of particles at render time with the render multiplier

(6) SDK: Declarations
 #define LXsPARTICLEATTR_SEED            "seed"
 #define LXsPARTICLEATTR_USEPOL          "usePol"
 #define LXsPARTICLEATTR_UPAXIS          "upAxis"
 #define LXsPARTICLEATTR_DEFORM          "deform"
 #define LXsPARTICLEATTR_SPACING         "spacing"

Particle features defined for vertices in the source. For legacy reasons these defines have a tableau prefix.

(7) SDK: Declarations
 #define LXiTBLX_PARTICLES        LXxID4('p','r','t','i')
 #define LXsTBLX_PARTICLE_POS    "pos"
 #define LXsTBLX_PARTICLE_XFRM   "xfrm"
 #define LXsTBLX_PARTICLE_ID     "id"
 #define LXsTBLX_PARTICLE_SIZE   "size"
 #define LXsTBLX_PARTICLE_VEL    "vel"
 #define LXsTBLX_PARTICLE_MASS   "mass"
 #define LXsTBLX_PARTICLE_FORCE  "force"
 #define LXsTBLX_PARTICLE_AGE    "age"
 #define LXsTBLX_PARTICLE_PATH   "pathLength"
 #define LXsTBLX_PARTICLE_DISS   "dissolve"
 #define LXsTBLX_PARTICLE_RATE   "rate"
 #define LXsTBLX_PARTICLE_ITEM   "item"
 #define LXsTBLX_PARTICLE_ANGVEL "angVel"
 #define LXsTBLX_PARTICLE_TORQUE "torque"
 #define LXsTBLX_PARTICLE_PPREV  "posPrev"
 #define LXsTBLX_PARTICLE_LUM    "lum"
 #define LXsTBLX_PARTICLE_RGB    "rgb"
 
 #define LXiTBLX_COLLISION        LXxID4('c','o','l','l')
 #define LXsTBLX_COLLISION_VAL   "val"
 #define LXsTBLX_COLLISION_POS   "pos"
 #define LXsTBLX_COLLISION_NORM  "norm"
 #define LXsTBLX_COLLISION_COUNT "count"
 #define LXsTBLX_COLLISION_TIME  "time"

The ReplicatorEnumerator interface allows traversal of the members of the particle item.

(8) SDK: Declarations
 #define LXu_REPLICATORENUMERATOR        "01EC90A9-924F-4475-BA6A-FFF8A2691CD5"

The Enumerate() method takes a visitor and calls its Evaluate() method for each replicant of a replicator. During each Evaluate() call, the visitor can get information about the member. If 'localXform' is true, then the transforms from the Replicator Item & Particle Source are *not* applied.

(9) SDK: ILxReplicatorEnumerator interface
         LXxMETHOD(  LxResult,
 Enumerate) (
         LXtObjectID              self,
         LXtObjectID              visitor,
         LXtObjectID              chan,
         int                      localXform);
 
         LXxMETHOD(  LxResult,
 Item) (
         LXtObjectID              self,
         void                   **ppvObj);
 
         LXxMETHOD(  void,
 Position) (
         LXtObjectID              self,
         LXtVector                pos);
 
         LXxMETHOD(  void,
 Orientation) (
         LXtObjectID              self,
         LXtMatrix                mx);
 
         LXxMETHOD(  float,
 Id) (
         LXtObjectID              self);
 
         LXxMETHOD(  float,
 Dissolve) (
         LXtObjectID              self);

The two Enum() methods correspond to the two ways of acquiring an enumerator. The channel-read object is not needed for enumerators allocated as part of a modifier.

(10) User Class: ReplicatorEnumerator method
         LxResult
 Enum (
         CLxImpl_AbstractVisitor *visitor,
         ILxUnknownID             chan,
         bool                     localXform)
 {
         CLxInst_OneVisitor<CLxGenericVisitor>    gv;
 
         gv.loc.vis = visitor;
         return Enumerate (gv, chan, localXform ? 1 : 0);
 }
 
         LxResult
 Enum (
         CLxImpl_AbstractVisitor *visitor,
         bool                     localXform)
 {
         CLxInst_OneVisitor<CLxGenericVisitor>    gv;
 
         gv.loc.vis = visitor;
         return Enumerate (gv, 0, localXform ? 1 : 0);
 }
 
         bool
 GetItem (
         CLxLoc_Item             &item)
 {
         LXtObjectID              obj;
 
         if (LXx_OK (Item (&obj)))
                 return item.take (obj);
 
         item.clear ();
         return false;
 }

Empty ReplicatorEnumerator Python user class.

(11) PY: ReplicatorEnumerator method
 pass

Particle Simulations

A particle simulation computes the state of a set of particles as they evolve over time. The state is affected by filter layers, which can create and destroy particles, and can alter their state vector.

Evaluation State

The particle evaluation frame object maintains the state of the particle simulation.

There can be any number of particles, but all of them contain the same information in the form of a vector of floats. This method gets the description of the complete vertex vector for the simulation.

(12) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( LXtObjectID,
 VertexDescription) (
         LXtObjectID              self);

MaxCount() returns the number of particle slots, although not all will contain active particles. AliveCount() returns the number of active particles.

(13) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( unsigned,
 MaxCount) (
         LXtObjectID              self);
 
         LXxMETHOD( unsigned,
 AliveCount) (
         LXtObjectID              self);

AddParticle() adds a new particle with an initial state given by the vector. After this the tags can be set through the StringTag interface. The index of the particle is returned.

(14) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( LxResult,
 AddParticle) (
         LXtObjectID              self,
         const float             *state,
         unsigned                *index);

This changes an active particle into an inactive one. Inactive particle slots may be reused for new particles.

(15) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( LxResult,
 KillParticle) (
         LXtObjectID              self,
         unsigned                 index);

This method tests if a given particle is active.

(16) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( unsigned,
 IsAlive) (
         LXtObjectID              self,
         unsigned                 index);

The state vector for a particle can be read and set using these methods. For GetVector() the client has to pass a sufficient buffer to recieve the requested state vector.

(17) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( LxResult,
 GetVector) (
         LXtObjectID              self,
         unsigned                 index,
         float                   *vector);
 
         LXxMETHOD( LxResult,
 SetVector) (
         LXtObjectID              self,
         unsigned                 index,
         const float             *vector);

Particles can also be accessed in runs to improve performance. These methods return runs of 'alive' flags and vector values starting from particle index 'first' and containing 'count' particles.

(18) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( LxResult,
 AliveRun) (
         LXtObjectID              self,
         unsigned                 first,
         const unsigned         **alive,
         unsigned                *count);
 
         LXxMETHOD( LxResult,
 VectorRun) (
         LXtObjectID              self,
         unsigned                 first,
         float                  **values,
         unsigned                *count);

The 'alive' run holds state bits for each particle. Newborn particles are alive+changed, just killed particles are dead+changed. The changed flag is cleared at the start of each timestep.

(19) SDK: Declarations
 #define LXfFRAME_ALIVE           0x01
 #define LXfFRAME_CHANGED         0x02

Find nearest particles to the given 3D position. The maxDist is the range to search for particles, and can be -1 for searching everywhere. The maxCount is the maximum number to return, and the index and dist arrays should be at least that long. If you are giving an existing particle position you need to ask for one more particle than you want since the particle itself will be first.

(20) SDK: ILxParticleEvalFrame interface
         LXxMETHOD( LxResult,
 Neighbors) (
         LXtObjectID              self,
         LXtFVector               pos,
         double                   maxDist,
         int                      maxCount,
         unsigned                *index,
         double                  *dist,
         unsigned                *count);

(21) SDK: Declarations
 #define LXu_PARTICLEEVALFRAME   "1AC26263-A422-4B17-A929-2F616037754F"

(22) User Class: ParticleEvalFrame method

(23) PY: ParticleEvalFrame method
 pass

Particle Filters

Process layers are filters that are called in sequence to generate the simulation.

The filter provides a peek at the vertex features that it wants to read or write. For all operations on the particle state, these are the only features that the filter will see. The particle frame will remap the full state into the state requested by the filter.

If 'full' is null, then this should return the features required by this filter. If 'full' is non-null, then it contains the full set of features in the simulation and the filter can return a second vertex containing any of the features in the full set. Filters that don't want optional features can just return the same required vertex for both cases.

(24) SDK: ILxParticleFilter interface
         LXxMETHOD( LXtObjectID,
 Vertex) (
         LXtObjectID              self,
         LXtObjectID              full);

A filter can have one of several types. These determine which processing method will be used for the filter. Additional flags can be set to select the stage of the process where the filter wants to run.

(25) SDK: ILxParticleFilter interface
         LXxMETHOD( unsigned,
 Flags) (
         LXtObjectID              self);

(26) SDK: Declarations
 #define LXiPFILT_FRAME          0x00
 #define LXiPFILT_RUN            0x01
 #define LXiPFILT_PARTICLE       0x02
 #define LXiPFILT_NEW_PARTICLE   0x03
 #define LXmPFILT_TYPE           0x0F
 #define LXfPFILT_PRESTAGE       0x10
 #define LXfPFILT_DERIVSTAGE     0x20
 #define LXfPFILT_POSTSTAGE      0x40
 #define LXfPFILT_ALIVERUN       0x80

Initialize() is called at start of simulation and gets the frame object and start time.

(27) SDK: ILxParticleFilter interface
         LXxMETHOD( LxResult,
 Initialize) (
         LXtObjectID              self,
         LXtObjectID              vertex,
         LXtObjectID              frame,
         double                   time);

Step() is called for each new time step. The 'other' pointer is another instance of this same filters, and contains the evaluation state for this timestep.

(28) SDK: ILxParticleFilter interface
         LXxMETHOD( LxResult,
 Step) (
         LXtObjectID              self,
         LXtObjectID              other,
         double                   dt);

Cleanup() is called when the simulation is complete.

(29) SDK: ILxParticleFilter interface
         LXxMETHOD( void,
 Cleanup) (
         LXtObjectID              self);

Frame() is called for filters of type LXi_PFILT_FRAME and allows the filter to process the entire frame in any manner. This is the only way to add or remove particles, for example.

(30) SDK: ILxParticleFilter interface
         LXxMETHOD( LxResult,
 Frame) (
         LXtObjectID              self,
         unsigned                 stage);

Run() is called for filters of type LXi_PFILT_RUN and allows the filter to process a run of particles starting at 'base' for 'count' particles. The values arrays that are passed in match the description of the requested vertex vector. Some of these particles may be dead, but it may be faster to process them than to test. The alive state can be read by setting the ALIVERUN flag.

(31) SDK: ILxParticleFilter interface
         LXxMETHOD( LxResult,
 Run) (
         LXtObjectID              self,
         unsigned                 stage,
         float                  **values,
         const unsigned          *alive,
         unsigned                 base,
         unsigned                 count);

Particle() is called for filters of type LXi_PFILT_PARTICLE or LXi_PFILT_NEW_PARTICLE and allows the filter to process a single particle at a time. Again the vector passed matches the request by the filter. For the 'NEW' case this method is only called for particles added by a previous particle filter. This can return LXePARTICLE_KILL to kill the particle.

(32) SDK: ILxParticleFilter interface
         LXxMETHOD( LxResult,
 Particle) (
         LXtObjectID              self,
         unsigned                 stage,
         float                   *vertex);

(33) SDK: Declarations
 #define LXu_PARTICLEFILTER      "04A3C0C5-1C5C-43F5-8559-ACF3BAE79C0B" 
 #define LXsPKG_PFILT_CHANNEL    "particleFilter.channel"
 
 #define LXsPFILT_ENABLECHANNEL  "pfiltEnable"
 
 #define LXePARTICLE_KILL        LXxGOODCODE(LXeSYS_PSYS,1)

(34) User Class: ParticleFilter method

Empty ParticleFilter Python user class.

(35) PY: ParticleFilter method
 pass

Particle Co-Operators

A particle co-operator is an item that can be linked to a particle operator. This allows it to be part of the process for a specific operator. Init and cleanup are called at the start and end of the sim.

(36) SDK: ILxParticleCoOperator interface
         LXxMETHOD( LxResult,
 Initialize) (
         LXtObjectID              self,
         LXtObjectID              eval);
 
         LXxMETHOD( LxResult,
 Cleanup) (
         LXtObjectID              self);

The operator is called at the start of the step with the time, and for each particle.

(37) SDK: ILxParticleCoOperator interface
         LXxMETHOD( LxResult,
 Step) (
         LXtObjectID              self,
         double                   dT);
 
         LXxMETHOD( LxResult,
 Particle) (
         LXtObjectID              self);

(38) SDK: Declarations
 #define LXu_PARTICLECOOPERATOR  "DFBCF67B-C327-496E-8A30-20B64C31A9BB" 
 #define LXsPKG_PCOOP_CHANNEL    "particleCoop.channel"
 #define LXsGRAPH_PARTICLEOP     "particleOp"

(39) User Class: ParticleCoOperator method

Empty ParticleCoOperator Python user class.

(40) PY: ParticleCoOperator method
 pass

Point Cache

Other items can function as point caches. If they provide a PointCacheItem interface then the particle simulation can store its results into the cache. The cache item will typically also have a ParticleItem interface to allow the cache to be used.

Prepare
Allow the point cache to attach itself to an evaluation context by adding channels.
Initialize
This is called to start writing frames into the cache. Any previous contents of the cache should be cleared. This is passed the vertex description for the particle features to be saved, and the attributes for reading added channels. It also gets the start time and sample rate.
SaveFrame
This saves a single frame of particle data to the cache. The object is a particle object with a TableauSurface interface.
Cleanup
This is called when writing data to the cache is complete.

(41) SDK: ILxPointCacheItem interface
         LXxMETHOD( LxResult,
 Prepare) (
         LXtObjectID              self,
         LXtObjectID              eval,
         unsigned                *index);
 
         LXxMETHOD( LxResult,
 Initialize) (
         LXtObjectID              self,
         LXtObjectID              vdesc,
         LXtObjectID              attr,
         unsigned                 index,
         double                   time,
         double                   sample);
 
         LXxMETHOD( LxResult,
 SaveFrame) (
         LXtObjectID              self,
         LXtObjectID              pobj,
         double                   time);
 
         LXxMETHOD( LxResult,
 Cleanup) (
         LXtObjectID              self);

(42) SDK: Declarations
 #define LXu_POINTCACHEITEM      "10930C44-62D3-42D1-BD6B-8FE015D9C566" 
 #define LXsGRAPH_POINTCACHE     "pointCache"

(43) User Class: PointCacheItem method

Empty PointCacheItem Python user class.

(44) PY: PointCacheItem method
 pass

Simulation Item

(45) SDK: Declarations
 #define LXsITYPE_PSIM            "particleSim"
 #define LXsICHAN_PSIM_ENABLE     "enable"
 #define LXsICHAN_PSIM_SAMPLES    "samples"
 #define LXsICHAN_PSIM_ADDAGE     "addAge"
 #define LXsICHAN_PSIM_ADDPATH    "addPath"
 #define LXsICHAN_PSIM_STOREFORCE "storeForce"
 #define LXsICHAN_PSIM_STOREMASS  "storeMass"
 #define LXsICHAN_PSIM_AGEKILL    "ageKill"
 #define LXsICHAN_PSIM_AGEMAX     "ageMax"
 #define LXsICHAN_PSIM_AGEEXTEND  "ageExtend"
 #define LXsICHAN_PSIM_GRAVENABLE "gravEnable"
 #define LXsICHAN_PSIM_GRAVAXIS   "gravAxis"
 #define LXsICHAN_PSIM_GRAVITY    "gravity"
 #define LXsICHAN_PSIM_DRAGENABLE "dragEnable"
 #define LXsICHAN_PSIM_DRAG       "drag"
 #define LXsICHAN_PSIM_COLOR      "color"
 #define LXsICHAN_PSIM_TGROUP     "tgroup"
 #define LXsICHAN_PSIM_PCOUNT     "pCount"
 #define LXsICHAN_PSIM_NTHREAD    "nThread"
 #define LXsICHAN_PSIM_CACHEMEM   "cacheMem"
 
 #define LXsGRAPH_PSIM            "particleSim"

Particle Service

The particle service provides methods for accessing particle-related information.

(46) SDK: Declarations
 #define LXa_PARTICLESERVICE     "particleservice"
 #define LXu_PARTICLESERVICE     "34928BF8-3917-47EF-8350-DD3A3DDED7EE"

(47) SDK: ILxParticleService interface
         LXxMETHOD(  LxResult,
 ScriptQuery) (
         LXtObjectID              self,
         void                   **ppvObj);

Get an enumerator for a replicator. This requires a valid replicator item, and will need an evaluated channel-read object to enumerate.

(48) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 GetReplicatorEnumerator) (
         LXtObjectID              self,
         LXtObjectID              replicatorItem,
         void                   **ppvObj);

Enumerate the particle features added to an item. Any item can set the USEFEATURES server tag to allow features to be added. For each feature the visitor can read the ident string and channel offset.

(49) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 EnumParticleFeatures) (
         LXtObjectID              self,
         LXtObjectID              item,
         LXtObjectID              visitor);

(50) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 FeatureIdent) (
         LXtObjectID              self,
         const char             **ident);

(51) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 FeatureOffset) (
         LXtObjectID              self,
         unsigned                *offset);

(52) User Service Class: ParticleService method
         LxResult
 EnumFeatures (
         ILxUnknownID             item,
         CLxImpl_AbstractVisitor *visitor)
 {
         CLxInst_OneVisitor<CLxGenericVisitor>  gv;
 
         gv.loc.vis = visitor;
         return EnumParticleFeatures (item, gv);
 }

If you have a triGroup object with positional data and other particle features, you can convert it to a particle object with this function.

(53) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 TriGroupToParticle) (
         LXtObjectID              self,
         LXtObjectID              triGroup,
         void                   **ppvObj);

You can also blend two tri-groups representing particle state. Group 0 will be changed based on the amount, with 0.0 being no change and 1.0 being complete replacement.

(54) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 TriGroupBlend) (
         LXtObjectID              self,
         LXtObjectID              triGroup0,
         LXtObjectID              triGroup1,
         double                   blend);

These methods are provided for accessing a replicator enumerator as part of a modifier. The channel-read object in this case should be NULL.

(55) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 EnumeratorPrepare) (
         LXtObjectID              self,
         LXtObjectID              eval,
         LXtObjectID              replItem,
         unsigned                *index);
 
         LXxMETHOD( LxResult,
 EnumeratorEvaluate) (
         LXtObjectID              self,
         LXtObjectID              attr,
         unsigned                 index,
         void                   **ppvObj);

Get a particle object from any particle item. Takes an eval channel read object.

(56) SDK: ILxParticleService interface
         LXxMETHOD( LxResult,
 ItemToParticle) (
         LXtObjectID              self,
         LXtObjectID              item,
         LXtObjectID              chanRead,
         void                   **ppvObj);

Empty particle service Python user class.

(57) PY: ParticleService method
 pass