Search code examples
maxscript

accessing paticleIndex of generated pf source


when creating a PF Source node in a scene I can select this system in maxscript and iterate through all the particles using a for loop and pf.particleIndex

However, when I use maxscript to loop through the geometry in a scene and generate a PF Source for each object pf.NumParticles() returns 0 and pf.particlePosition of pf.particleIndex = i returns 0 0 0 for coord.x, .y and .z

geo = #()

for OBJ in Geometry do (
    append geo OBJ
)

for OBJ in geo do ( 
    with undo off (
        -- define color for current object
        r = random 128 192
        g = random 128 192
        b = random 128 192

        -- create a particle system
        pf = PF_Source() 
        pf.Quantity_Viewport = 100

        ParticleFlow.BeginEdit()

        -- enable render
        rp = RenderParticles()
        pf.AppendAction rp

        -- create birth
        a1 = Birth()
        a1.Amount = random 1000 2000
        a1.Emit_Start = 0
        a1.Emit_Stop = 0

        -- position to surface of current object
        a2 = Position_Object()
        a2.Location = 3
        a2.Emitter_Objects = #(OBJ)

        -- show in viewport
        a3 = DisplayParticles()
        a3.type = 2
        a3.color = (color r g b)

        -- add the event to the flow
        e1 = Event()
        e1.AppendAction a1
        e1.AppendAction a2
        e1.AppendAction a3

        ParticleFlow.EndEdit()

        -- create the "complete" particle system
        pf.appendInitialActionList e1

        -- get particle coordinates
        particleAmount = pf.NumParticles()
        print particleAmount
        for i = 1 to particleAmount do (
            pf.particleIndex = i
            coord = pf.particlePosition
            print coord
        )
    )
)

-- garbage collection
gc()

Is there some way to, I don't know, refresh the PF Source in the script so it is aware of there being particles in the system.


Solution

  • There's a known issue with PF, when creating it with maxscript you have to reload it, for example using hold/fetch:

    geo = geometry as array
    
    for OBJ in geo do
    (
        -- create a particle system
        local pf = PF_Source Quantity_Viewport:100
        -- save its identifier for later
        local handle = pf.iNode.handle
        local particleAmount = random 1000 2000
    
        ParticleFlow.BeginEdit()
    
        -- enable render
        pf.AppendAction (RenderParticles())
    
        -- add the event to the flow
        local e1 = Event()
        -- create birth
        e1.AppendAction (Birth Amount:particleAmount Emit_Start:0 Emit_Stop:0)
        -- position to surface of current object
        e1.AppendAction (Position_Object Location:3 Emitter_Objects:#(OBJ))
        -- show in viewport
        e1.AppendAction (DisplayParticles type:2 color:(random [128,128,128] [192,192,192]))
    
        ParticleFlow.EndEdit()
    
        -- create the "complete" particle system
        pf.appendInitialActionList e1
    
        -- force particle flow to update particles
        holdMaxFile()
        fetchMaxFile quiet:true
    
        -- get the particle flow node again
        pf = maxOps.getNodeByHandle handle
    
        -- get particle coordinates
        for i = 1 to particleAmount do
        (
            pf.particleID = i
            coord = pf.particlePosition
            format "index: %, pos: %\n" pf.particleIndex coord
        )
    )
    

    On the other hand if you only need random particles on surface, PArray might be a better solution for you:

    geo = geometry as array
    
    for OBJ in geo do
    (
        local particleAmount = random 1000 2000
        local pa = PArray emitter:obj formation:0 speed:0 Birth_Rate:particleAmount viewPercent:100 \
            Emitter_Start:0f Emitter_Stop:0f wirecolor:(random [128,128,128] [192,192,192])
    
        for i = 1 to particleAmount do
            format "index: %, pos: %\n" i (particlePos pa i)
    )