Python Tools - Session 12B

Ring of Fire Particle FX Tool (Part 1)

2020.04.02 - Session 12B

Session Navigation

Overview

Part 1 of the Ring of Fire tool creates an animated particle effect using emitters distributed around a NURBS circle. Users control the number of emitters and radius through a Qt UI. The tool creates nParticle systems connected to emitters placed at each CV (control vertex) of the circle, then parents everything together for unified animation.

Key Concepts

  • NURBS circle creation - Create circles with specific normals and section counts
  • CV position queries - Get control vertex positions to place emitters
  • nParticle systems - Create particle effects and connect them to emitters
  • Dynamic connections - Use connectDynamic to link emitters to particle systems
  • Parenting hierarchy - Parent emitters under the NURBS circle for unified transforms
Note

This session uses a companion ring_of_fire.ui file created in Qt Designer for the number-of-emitters and radius controls.

Source Code - ring_of_fire_part1.py

"""
#############################################################################
filename    ring_of_fire.py
author      [author]
course      [course]
Brief Description:
    Particle FX Tool
    To be used in conjunction with "ring_of_fire.ui" - place them next to each other
    Session 12B - Apr 2 2020
#############################################################################
"""

import pymel.core as pm
import os

from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtUiTools

from shiboken2 import wrapInstance

import maya.OpenMayaUI as omui

pm.newFile(force=True)

def get_maya_window():
    """Return the main Maya window
    """
    main_window_ptr = omui.MQtUtil.mainWindow()
    return wrapInstance(long(main_window_ptr), QtWidgets.QWidget)

def get_script_dir():
    """Returns the directory where the current script lives
    """
    script_file = os.path.abspath(__file__)
    return os.path.dirname(script_file)

class RingOfFireUI(QtWidgets.QDialog):

    def __init__(self, parent=get_maya_window()):
        super(RingOfFireUI, self).__init__(parent)

        self.setWindowTitle('Ring of Fire FX')

        ui_file_path = os.path.join(get_script_dir(), 'ring_of_fire.ui')

        qfile_object = QtCore.QFile(ui_file_path)
        qfile_object.open(QtCore.QFile.ReadOnly)

        loader = QtUiTools.QUiLoader()
        self.ui = loader.load(qfile_object, parentWidget=self)

        self.ui.btnCreateFX.clicked.connect(self.create_ring)

        qfile_object.close()
        self.show()

    def create_ring(self):
        """Query UI values and pass them to the creation function
        """
        number_of_emitters = self.ui.spnNumberOfEmitters.value()
        radius = self.ui.spnRadius.value()
        create(number_of_emitters, radius)

def create(emitter_count, hoop_radius):
    """Creates an animated particle FX hoop
    """
    print emitter_count, hoop_radius

    # Create the NURBS circle, facing sideways
    hoop = pm.circle(normal=[1,0,0], radius=hoop_radius, sections=emitter_count)[0]

    # Create a single particle effect
    fx = pm.nParticle()

    # Loop through each CV position
    for pos in hoop.getCVs():
        print pos

        # Create a new emitter at the desired location
        new_emitter = pm.emitter(pos=pos)

        # Connect the emitter to our particle effect
        pm.connectDynamic(fx, emitters=new_emitter)

        # Parent the emitter to the circle
        pm.parent(new_emitter, hoop)

def run():
    """Checks to see if the UI exists, and closes it
    """
    for ui_item in QtWidgets.qApp.allWidgets():
        if type(ui_item).__name__ == 'RingOfFireUI':
            ui_item.close()
    RingOfFireUI()
← Prev Next ->