pythonmayamaya-api

use OpenMaya to give particles specific translate and rotate values


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.


Solution

  • 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)