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: ParticleItem::Prepare, etc.
         LXxMETHOD( LxResult,
 Prepare) (
         LXtObjectID              self,
         LXtObjectID              eval,
         unsigned                *index);
 
         LXxMETHOD( LxResult,
 Evaluate) (
         LXtObjectID              self,
         LXtObjectID              attr,
         unsigned                 index,
         void                   **ppvObj);

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

(3) SDK: CLxUser_ParticleItem::Alloc 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: empty ParticleItem user class
 pass

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

(5) SDK: LXsPKG_READPARTICLES, etc. defines
 #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: LXsPARTICLEATTR_SEED, etc. defines
 #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: LXiTBLX_PARTICLES, etc. defines
 #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: LXu_REPLICATORENUMERATOR define
 #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: ReplicatorEnumerator::Enumerate, etc.
         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);
 
         LXxMETHOD (float,
 GroupId) (
         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) SDK: CLxUser_ReplicatorEnumerator::Enum 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: empty ReplicatorEnumerator user class
 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: ParticleEvalFrame::VertexDescription
         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: ParticleEvalFrame::MaxCount, etc.
         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: ParticleEvalFrame::AddParticle
         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: ParticleEvalFrame::KillParticle
         LXxMETHOD( LxResult,
 KillParticle) (
         LXtObjectID              self,
         unsigned                 index);

This method tests if a given particle is active.

(16) SDK: ParticleEvalFrame::IsAlive
         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: ParticleEvalFrame::GetVector, etc.
         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: ParticleEvalFrame::AliveRun, etc.
         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: LXfFRAME_ALIVE, etc. defines
 #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: ParticleEvalFrame::Neighbors
         LXxMETHOD( LxResult,
 Neighbors) (
         LXtObjectID              self,
         LXtFVector               pos,
         double                   maxDist,
         int                      maxCount,
         unsigned                *index,
         double                  *dist,
         unsigned                *count);

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

(22) SDK: empty ParticleEvalFrame User Class

(23) PY: empty ParticleEvalFrame user class
 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: ParticleFilter::Vertex
         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: ParticleFilter::Flags
         LXxMETHOD( unsigned,
 Flags) (
         LXtObjectID              self);

(26) SDK: LXiPFILT_FRAME, etc. defines
 #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: ParticleFilter::Initialize
         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: ParticleFilter::Step
         LXxMETHOD( LxResult,
 Step) (
         LXtObjectID              self,
         LXtObjectID              other,
         double                   dt);

Cleanup() is called when the simulation is complete.

(29) SDK: ParticleFilter::Cleanup
         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: ParticleFilter::Frame
         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: ParticleFilter::Run
         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: ParticleFilter::Particle
         LXxMETHOD( LxResult,
 Particle) (
         LXtObjectID              self,
         unsigned                 stage,
         float                   *vertex);

(33) SDK: LXu_PARTICLEFILTER, etc. defines
 #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) SDK: empty ParticleFilter User Class

Empty ParticleFilter Python user class.

(35) PY: empty ParticleFilter user class
 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: ParticleCoOperator::Initialize, etc.
         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: ParticleCoOperator::Step, etc.
         LXxMETHOD( LxResult,
 Step) (
         LXtObjectID              self,
         double                   dT);
 
         LXxMETHOD( LxResult,
 Particle) (
         LXtObjectID              self);

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

(39) SDK: empty ParticleCoOperator User Class

Empty ParticleCoOperator Python user class.

(40) PY: empty ParticleCoOperator user class
 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: PointCacheItem::Prepare, etc.
         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: LXu_POINTCACHEITEM, etc. defines
 #define LXu_POINTCACHEITEM      "10930C44-62D3-42D1-BD6B-8FE015D9C566" 
 #define LXsGRAPH_POINTCACHE     "pointCache"

(43) SDK: empty PointCacheItem User Class

Empty PointCacheItem Python user class.

(44) PY: empty PointCacheItem user class
 pass

Simulation Item

(45) SDK: LXsITYPE_PSIM, etc. defines
 #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: LXa_PARTICLESERVICE, etc. defines
 #define LXa_PARTICLESERVICE     "particleservice"
 #define LXu_PARTICLESERVICE     "34928BF8-3917-47EF-8350-DD3A3DDED7EE"

(47) SDK: ParticleService::ScriptQuery
         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: ParticleService::GetReplicatorEnumerator
         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: ParticleService::EnumParticleFeatures
         LXxMETHOD( LxResult,
 EnumParticleFeatures) (
         LXtObjectID              self,
         LXtObjectID              item,
         LXtObjectID              visitor);

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

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

(52) SDK: CLxUser_ParticleService::EnumFeatures 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: ParticleService::TriGroupToParticle
         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: ParticleService::TriGroupBlend
         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: ParticleService::EnumeratorPrepare, etc.
         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: ParticleService::ItemToParticle
         LXxMETHOD( LxResult,
 ItemToParticle) (
         LXtObjectID              self,
         LXtObjectID              item,
         LXtObjectID              chanRead,
         void                   **ppvObj);

Empty particle service Python user class.

(57) PY: empty Service.Particle user class
 pass