use OpenMaya to give particles specific translate and rotate values

Question:

I’m struggling with OpenMaya here.

I want to be able to take transform information from a list of locators and plug these values to particles shapes.
The goal is to use this over 25000 locators, so I can’t create a particle system for each instance. I really need to store position and rotation values to the particles themselves.

To do that I started to dive into OpenMaya… (╯°□°)╯︵ ┻━┻

Anyway, the problem I’m facing now is that my scene crashes every time I launch this script and I can’t figure out what I did wrong. I think I’m pretty close, but crashing Maya is not considered a victory.

import pymel.core as pm
import maya.OpenMaya as om
import maya.OpenMayaFX as omfx

import random

### A short script to create the scene with bunch of locators with random pos rot

numOfLoc = 5 # this number will eventually be set 25000 when the script will work.

# create locators with random position location(for test)
def create_gazillion_locators(numOfLoc):
    for i in range(0, numOfLoc):
        # to create variation
        tx = random.uniform(-10, 10)
        ty = random.uniform(0, 5)
        tz = random.uniform(-10, 10)
        rx = random.uniform(0, 360)
        ry = random.uniform(0, 360)
        rz = random.uniform(0, 360)
        
        pm.spaceLocator()
        pm.move(tx, ty, tz)
        pm.rotate(rx, ry, rz, ws=True)

# Select locators 
def select_locators():
    pm.select(cl=True)
    loc_selection = pm.listRelatives(pm.ls(type = 'locator'), p=True)
    pm.select(loc_selection, r=True)

    return loc_selection

# delete the locators
def clean_the_scene():
        #del locators (for testing purpiose)
        sel = select_locators()
        if sel is not None:
            pm.delete(sel)


clean_the_scene()
create_gazillion_locators(numOfLoc)



### Actual script

# Found this on the internet. it seems to be more neat
class Vector(om.MVector):
    def __str__(self):
        return '{0}, {1}, {2}'.format(self.x, self.y, self.z)

    def __repr__(self):
        return '{0}, {1}, {2}'.format(self.x, self.y, self.z)    


# OpenMaya treatment
sel = select_locators()

mSel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(mSel)
mSel_iter = om.MItSelectionList(mSel)
mSel_DagPath = om.MDagPath()

# bvariables to store the transform in
pos_array = om.MVectorArray()
rot_array = om.MVectorArray()

mLoc = om.MObject()


# Main loop of selection iterator.
while not mSel_iter.isDone():

    # Get list of selected
    mSel_iter.getDagPath(mSel_DagPath)

    mSel_iter.getDependNode(mLoc)

    dep_node_name = om.MFnDependencyNode(mLoc).name()

    transl = pm.getAttr('{}.translate'.format(dep_node_name))
    rotate = pm.getAttr('{}.rotate'.format(dep_node_name))
    
    print(dep_node_name)   

    print(Vector(transl[0], transl[1], transl[2]))
    print(Vector(rotate[0], rotate[1], rotate[2]))

    pos_array.append(Vector(transl[0], transl[1], transl[2]))
    rot_array.append(Vector(rotate[0], rotate[1], rotate[2]))
        
    mSel_iter.next()

# Up untill there it seems to work ok.

nparticles_transform, nparticles_shape = pm.nParticle(position = pos_array)


pm.setAttr('nucleus1.gravity', 0.0)


nparticles_shape.computeRotation.set(True)


pm.addAttr(nparticles_shape, ln = 'rotationPP', dt = 'vectorArray')
pm.addAttr(nparticles_shape, ln = 'rotationPP0', dt = 'vectorArray')
pm.particleInstancer(nparticles_shape, name = p_instancer, edit = True, rotation = "rotationPP")


particle_fn = omfx.MFnParticleSystem(nparticles_shape.__apimobject__())
particle_fn.setPerParticleAttribute('rotationPP', rot_array)
particle_fn.setPerParticleAttribute('rotationPP0', rot_array)


I read lots of things, went through the stack and google, I based my script on several other stuff I found/learnt (I listened to the OpenMaya course on Youtube by Chayan Vinayak)… But I’ve had a hard time understanding the OpenMaya documentation though.

Asked By: segretovfx

||

Answers:

I’ve made a couple of changes to make it work. I didn’t check the particle setup though. In fact, the main problem is mixing two different APIs. Either stick to OpenMaya (or even OpenMaya v2.0) or PyMEL.

import pymel.core as pm
import maya.OpenMaya as om
import maya.OpenMayaFX as omfx

import random

### A short script to create the scene with bunch of locators with random pos rot

numOfLoc = 5  # this number will eventually be set 25000 when the script will work.

# create locators with random position location(for test)
def create_gazillion_locators(num_of_loc):
    for i in range(num_of_loc):
        # to create variation
        tx = random.uniform(-10, 10)
        ty = random.uniform(0, 5)
        tz = random.uniform(-10, 10)
        rx = random.uniform(0, 360)
        ry = random.uniform(0, 360)
        rz = random.uniform(0, 360)

        pm.spaceLocator()
        pm.move(tx, ty, tz)
        pm.rotate(rx, ry, rz, ws=True)


# Select locators
def select_locators():
    pm.select(cl=True)
    loc_selection = pm.listRelatives(pm.ls(type="locator"), p=True)
    pm.select(loc_selection, r=True)

    return loc_selection


# delete the locators
def clean_the_scene():
    # del locators (for testing purpiose)
    sel = select_locators()
    if sel is not None:
        pm.delete(sel)


clean_the_scene()
create_gazillion_locators(numOfLoc)


### Actual script

# Found this on the internet. it seems to be more neat
class Vector(om.MVector):
    def __str__(self):
        return "{0}, {1}, {2}".format(self.x, self.y, self.z)

    def __repr__(self):
        return "{0}, {1}, {2}".format(self.x, self.y, self.z)


# OpenMaya treatment
sel = select_locators()

mSel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(mSel)
mSel_iter = om.MItSelectionList(mSel)
mSel_DagPath = om.MDagPath()

# bvariables to store the transform in
pos_array = []
rot_array = om.MVectorArray()

mLoc = om.MObject()


# Main loop of selection iterator.
while not mSel_iter.isDone():

    # Get list of selected
    mSel_iter.getDagPath(mSel_DagPath)

    mSel_iter.getDependNode(mLoc)

    dep_node_name = om.MFnDependencyNode(mLoc).name()

    transl = pm.getAttr("{}.translate".format(dep_node_name))
    rotate = pm.getAttr("{}.rotate".format(dep_node_name))

    print(dep_node_name)

    print(Vector(transl[0], transl[1], transl[2]))
    print(Vector(rotate[0], rotate[1], rotate[2]))

    pos_array.append((transl[0], transl[1], transl[2]))
    rot_array.append(Vector(rotate[0], rotate[1], rotate[2]))

    mSel_iter.next()

# Up untill there it seems to work ok.

nparticles_transform, nparticles_shape = pm.nParticle(position=pos_array)


pm.setAttr("nucleus1.gravity", 0.0)


nparticles_shape.computeRotation.set(True)


pm.addAttr(nparticles_shape, ln="rotationPP", dt="vectorArray")
pm.addAttr(nparticles_shape, ln="rotationPP0", dt="vectorArray")
# Create an instancer before trying to edit
instancer_node = pm.particleInstancer(nparticles_shape, name="p_instancer")
pm.particleInstancer(
    nparticles_shape, name=instancer_node, edit=True, rotation="rotationPP"
)

particle_fn = omfx.MFnParticleSystem(nparticles_shape.__apimobject__())
particle_fn.setPerParticleAttribute("rotationPP", rot_array)
particle_fn.setPerParticleAttribute("rotationPP0", rot_array)
Answered By: ababak

I had a look and there is no need to use any openmaya in this case if you need pymel anyway. I used cmds for the creation of the locators because it is a bit faster, so if execution speed is a problem, try to switch everything to cmds.
And I think there is no need to set the computeRotation because it’s only used during simulation.

    import pymel.core as pm
    import maya.cmds as cmds
    import random
    numOfLoc = 5000
        
    def c_create_gazillion_locators(num_of_loc):
        for i in range(num_of_loc):
            tx = random.uniform(-10, 10)
            ty = random.uniform(0, 5)
            tz = random.uniform(-10, 10)
            rx = random.uniform(0, 360)
            ry = random.uniform(0, 360)
            rz = random.uniform(0, 360)
              
            cmds.spaceLocator()
            cmds.move(tx, ty, tz)
            cmds.rotate(rx, ry, rz, ws=True)
    
    create_gazillion_locators(numOfLoc)
    
    locs = pm.ls(type="locator")
    locs = pm.listRelatives(locs, p=True)
    pos = []
    rot = []
    for loc in locs:
        pos.append(loc.translate.get())
        rot.append(loc.rotate.get())
        
    nparticles_transform, nparticles_shape = pm.nParticle(position=pos)
    pm.setAttr("nucleus1.gravity", 0.0)
    pm.addAttr(nparticles_shape, ln="rotationPP", dt="vectorArray")
    pm.addAttr(nparticles_shape, ln="rotationPP0", dt="vectorArray")
    rpp= pm.Attribute(nparticles_shape+".rotationPP")
    rpp0= pm.Attribute(nparticles_shape+".rotationPP0")
    rpp.set(rot)
    rpp0.set(rot)
Answered By: haggi krey
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.