[PYTHON] [Maya] Schreiben Sie einen benutzerdefinierten Knoten in Open Maya 2.0

Es ist leicht, den Workflow für die Implementierung benutzerdefinierter Knoten in Maya zu vergessen, daher schreibe ich ihn als Erinnerung.

Dieses Mal werde ich einen Knoten mit einfacher Eingabe / Ausgabe, einen Knoten, der Arrays verarbeiten kann, und einen Knoten, der zusammengesetzte Attribute verarbeiten kann, vorstellen und schließlich einen Knoten erstellen, der die Mittelpositionen mehrerer Punkte ausgibt.

Informationen zu benutzerdefinierten Knoten (Abhängigkeitsknoten, Knoten mit Eingabe / Ausgabe) finden Sie auf den folgenden offiziellen Seiten. Grundlagen des Abhängigkeitsdiagramm-Plugins Abhängigkeitsdiagramm-Plugin

Erforderliche Elemente für das Skript, das den Abhängigkeitsknoten definiert

Zumindest im Drehbuch

  1. Eine leere Funktion, die Sie auffordert, die Maya Python API 2.0 zu verwenden Wenn Sie eine Funktion namens "maya_useNewAPI ()" definieren, können Sie klar angeben, dass API 2.0 auf der Maya-Seite verwendet wird.

  2. Plugin-Ein- / Ausstiegspunkte Sie müssen "initializePlugin (mobject)" und "uninitializePlugin (mobject)" aufrufen, wenn Sie das Plugin laden und beenden. Hier definieren wir ** Knotenname **, ** Knoten-ID **, ** die folgenden 4/5 Funktionen **, ** Knotentyp **, ** Knotenklassifizierung ** (siehe unten).

  3. Erstellen Sie eine Funktion, die eine Instanz des Knotens zurückgibt

  4. Funktion, die die Attribute des Knotens initialisiert

  5. Knotenkörperklasse

Wird benötigt werden.

Knoten mit einfacher E / A.

Der Beispielknoten, den ich tatsächlich geschrieben habe, ist wie folgt. Es ist ein Knoten, der den im Eingabeattribut eingegebenen Float-Wert mit der sin-Funktion multipliziert und vom Ausgabeattribut ausgibt. Ich werde eins nach dem anderen erklären.

# -*- coding: utf-8 -*-
import maya.api.OpenMaya as om
import maya.api.OpenMayaUI as omui
import math, sys

# Maya API 2.Für die Verwendung von 0 erforderliche Funktionen
def maya_useNewAPI():
    pass

#Tatsächliche Klasse
class sampleNode(om.MPxNode):
    id = om.MTypeId(0x7f001) #Eindeutige ID https://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
    input = om.MObject()
    output = om.MObject()

    #Methode zum Zurückgeben einer Instanz
    @staticmethod
    def creator():
        return sampleNode()

    #Methode, die Maya bei der Initialisierung aufgerufen hat
    #Attribute festlegen
    @staticmethod
    def initialize():
        #Attribute werden mit der create-Methode einer Unterklasse der MFnAttribute-Klasse definiert.
        nAttr = om.MFnNumericAttribute()
        sampleNode.input = nAttr.create(
            'input', 'i', om.MFnNumericData.kFloat, 0.0)
        nAttr.storable = True
        nAttr.writable = True

        nAttr = om.MFnNumericAttribute()
        sampleNode.output = nAttr.create('output', 'o', om.MFnNumericData.kFloat, 0.0)
        nAttr.storable = True
        nAttr.writable = True

        #Führen Sie nach dem Definieren addAttribute von MPxNode aus
        sampleNode.addAttribute(sampleNode.input)
        sampleNode.addAttribute(sampleNode.output)
        #Stellen Sie außerdem den Ausgang so ein, dass er neu berechnet wird, wenn der Eingang geändert wird.
        sampleNode.attributeAffects( sampleNode.input, sampleNode.output)
        
    #Der Konstruktor ruft den übergeordneten Konstruktor auf
    def __init__(self):
        om.MPxNode.__init__(self)

    #Eine Methode, die von Maya aufgerufen wird, wenn der Wert eines Attributs berechnet wird
    def compute(self, plug, dataBlock):
        if(plug == sampleNode.output):
            dataHandle = dataBlock.inputValue(sampleNode.input)
            inputFloat = dataHandle.asFloat()
            result = math.sin(inputFloat) * 10.0
            outputHandle = dataBlock.outputValue(sampleNode.output)
            outputHandle.setFloat(result)
            dataBlock.setClean(plug)
            
    # http://help.autodesk.com/view/MAYAUL/2016/ENU/
    # api1.0 bedeutet MStatus, es sei denn, Sie weisen uns ausdrücklich an, den Stecker nicht zu verarbeiten.kUnknownParameter wird nicht zurückgegeben
    # api2.Bei 0 gibt es überhaupt keinen MStatus, sodass Sie ihn ignorieren können.

#Eine von Maya aufgerufene Funktion, die einen neuen Knoten registriert
def initializePlugin(obj):
    mplugin = om.MFnPlugin(obj)

    try:
        mplugin.registerNode('sampleNode', sampleNode.id, sampleNode.creator,
                             sampleNode.initialize, om.MPxNode.kDependNode)
    except:
        sys.stderr.write('Faled to register node: %s' % 'sampleNode')
        raise

#Von Maya beim Beenden des Plug-Ins aufgerufene Funktionen
def uninitializePlugin(mobject):
    mplugin = om.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode(sampleNode.id)
    except:
        sys.stderr.write('Faled to uninitialize node: %s' % 'sampleNode')
        raise

Klassen für Knoten definieren

Zuerst aus der Klassendefinition

class sampleNode(om.MPxNode):
    id = om.MTypeId(0x7f001) #Eindeutige ID https://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
    input = om.MObject()
    output = om.MObject()

Entscheiden Sie die ID des Plug-Ins. Weitere Informationen finden Sie unter Autodesk-Site. Normalerweise können Sie jedoch einen beliebigen Wert von 0x00000 bis 0x verwenden. Ich werde. Und ich habe die Eingabe- und Ausgabeattribute als Klassenfeld "intput" "output" vorbereitet.

Instanzgenerierungsfunktion

    #Methode zum Zurückgeben einer Instanz
    @staticmethod
    def creator():
        return sampleNode()

Eine Funktion, die eine Instanz eines Knotens erstellt. Sie können es außerhalb der Klasse schreiben, aber dieses Mal habe ich es als statische Methode der Klasse geschrieben.

Funktion zum Initialisieren von Attributen

#Methode, die Maya bei der Initialisierung aufgerufen hat
    #Attribute festlegen
    @staticmethod
    def initialize():
        #Attribute werden mit der create-Methode einer Unterklasse der MFnAttribute-Klasse definiert.
        nAttr = om.MFnNumericAttribute()
        sampleNode.input = nAttr.create(
            'input', 'i', om.MFnNumericData.kFloat, 0.0)
        nAttr.storable = True
        nAttr.writable = True

        nAttr = om.MFnNumericAttribute()
        sampleNode.output = nAttr.create('output', 'o', om.MFnNumericData.kFloat, 0.0)
        nAttr.storable = True
        nAttr.writable = True

        #Führen Sie nach dem Definieren addAttribute von MPxNode aus
        sampleNode.addAttribute(sampleNode.input)
        sampleNode.addAttribute(sampleNode.output)
        #Stellen Sie außerdem den Ausgang so ein, dass er neu berechnet wird, wenn der Eingang geändert wird.
        sampleNode.attributeAffects( sampleNode.input, sampleNode.output)

Dies ist der Teil, der in der vorherigen Liste "4. Funktionen zum Initialisieren von Knotenattributen" entspricht. Sie können es außerhalb der Klasse schreiben, aber es ist überladen, deshalb habe ich es als statische Methode geschrieben. Attribute werden unter Verwendung der entsprechenden Unterklassen der Klasse "MFnAttribute" definiert. Diesmal ist es ein Float-Wert, daher verwende ich "MFnNumericAttribute". Dreidimensionale Gleitkommawerte (Koordinaten usw.) und Bool-Werte sind ebenfalls dieses "MFnNumericAttribute". Den Winkel, die Entfernung und die Zeit finden Sie in "MFnUnitAttribute", die Matrix in "MFnMatrixAttribute" und die anderen in der folgenden Referenz. MFnAttribute Class Reference OpenMaya.MFnAttribute Class Reference

Geben Sie den Attributnamen, die Abkürzung, den Typ und den Anfangswert mit nAttr.create an. Ob nAttr.storable den Wert des Attributs in die Sicherungsdatei schreibt. Es gibt andere Eigenschaften wie "beschreibbar" und "lesbar". Stellen Sie sie daher entsprechend ein.

sampleNode.addAttribute (sampleNode.input) Fügt das erstellte Attribut dem Knoten hinzu. sampleNode.attributeAffects (sampleNode.input, sampleNode.output) Wenn sich der Wert des Eingabeattributs ändert, wird das Ausgabeattribut aktualisiert.

Berechnungskörpermethode

    #Eine Methode, die von Maya aufgerufen wird, wenn der Wert eines Attributs berechnet wird
    def compute(self, plug, dataBlock):
        if(plug == sampleNode.output):
            dataHandle = dataBlock.inputValue(sampleNode.input)
            inputFloat = dataHandle.asFloat()
            result = math.sin(inputFloat) * 10.0
            outputHandle = dataBlock.outputValue(sampleNode.output)
            outputHandle.setFloat(result)
            dataBlock.setClean(plug)
            

Die Berechnungsmethode ist die Methode, die aufgerufen wird, wenn die Berechnung abgeschlossen ist. Dieser Knoten berechnet Sin. Der Wert wird in Form eines Steckers übergeben. Weitere Informationen zu Steckern finden Sie in der Autodesk-Hilfe. Es ist lang, weil der DataBlock ein Handle benötigt, um die Eingabe abzurufen und der Ausgabe zuzuweisen, aber tatsächlich wird nur die Sin-Funktion berechnet.

Einstiegspunkt

#Eine von Maya aufgerufene Funktion, die einen neuen Knoten registriert
def initializePlugin(obj):
    mplugin = om.MFnPlugin(obj)

    try:
        mplugin.registerNode('sampleNode', sampleNode.id, sampleNode.creator,
                             sampleNode.initialize, om.MPxNode.kDependNode)
    except:
        sys.stderr.write('Faled to register node: %s' % 'sampleNode')
        raise

Ein Einstiegspunkt außerhalb der Klasse. Geben Sie den Knotennamen und die ID, die in der Klasse definierte Instanzmethode und den Knotentyp an.

Lauf

Laden Sie das Skript, das Sie aus Mayas Plug-in-Manager erstellt haben. Erstellen Sie einen Knoten mit "cmds.createNode (" sampleNode ")" oder "cmds.shadingNode (" sampleNode ", asUtility = True)" in Mayas Befehlszeile oder Skripteditor. Wenn Sie es mit letzterem erstellt haben, wird Ihr eigener Knoten auf der Registerkarte "Dienstprogramme" im Hypershade-Fenster angezeigt. 無題.png

Array-Attribute

Es gibt zwei Methoden: Die eine besteht darin, die Array-Daten als ein Attribut mit dem Plug zu verbinden, und die andere darin, den ** Array-Plug ** zu verwenden, in dem der Plug selbst ein Array ist.

Array-Stecker

Ändern Sie die Attributdefinition in der Methode "initialize" früher wie folgt. Sie können "nAttr.array = True" sehen, aber Sie können "-nextAvailable" mit dem Befehl "connectAttr" verwenden, indem Sie "nAttr.indexMatters = False" auf "False" setzen. Im Gegenteil, wenn es True ist, muss anscheinend der einzufügende Index angegeben werden.

    @staticmethod
    def initialize():
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.input = nAttr.create(
            'input', 'i', om.MFnNumericData.kFloat, 0.0)
        nAttr.storable = True
        nAttr.writable = True
        nAttr.readable = True
        nAttr.array = True  #hinzufügen
        nAttr.indexMatters = False  #hinzufügen

Als nächstes die Berechnungsmethode, die die eigentliche Berechnung übernimmt. Dieses Mal wird der Gesamtwert des Eingabearrays ausgegeben.

    def compute(self, plug, dataBlock):
        arrayDataHandle = dataBlock.inputArrayValue(
            sampleArrayNode.input
        )
        sum = 0
        while not arrayDataHandle.isDone():
            handle = arrayDataHandle.inputValue()
            v = handle.asFloat()
            sum += v
            arrayDataHandle.next()

        outhandle = dataBlock.outputValue( sampleArrayNode.output )
        outhandle.setFloat(sum)
        dataBlock.setClean(plug)

Wenn Sie einen Array-Plug verwenden, rufen Sie den "MArrayDataHandle" einmal mit "inputArrayValue" anstelle von "inputValue" ab. Da dies ein Iterator ist, erweitern Sie den Iterator mit "next ()" oder "jumpToLogicalElement ()" und ermitteln Sie den Wert des Elements des Arrays mit "arrayDataHandle.inputValue ()". Danach wird es in einen numerischen Wert umgewandelt und wie ein normaler Stecker berechnet.

01.jpg ↑ Die Konstante 1 + 2 + 3 + 4 = 10 wurde korrekt berechnet.

Zusammengesetzte Attribute

Ein zusammengesetztes Attribut ist eine Sammlung mehrerer Attribute. Komplexe dynamische Attribute ](https://help.autodesk.com/view/MAYAUL/2016/JPN/?guid=__files_Dependency_graph_plugins_Complex_Attributes_htm) In diesem Beispiel werden "Koordinaten und Gewichte" als zusammengesetzte Attribute und als Array-Stecker verwendet.

Es sieht aus wie das Bild unten im Knoteneditor. 02.png Die Implementierung des Klassenteils ist wie folgt. Die Einstiegspunkte usw. sind die gleichen wie im vorherigen Code (weggelassen).

class sampleArrayNode(om.MPxNode):
    #Eindeutige ID https://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
    id = om.MTypeId(0x7f011)
    input = om.MObject()
    output = om.MObject()
    #Untergeordnete Attribute
    position = om.MObject()
    weight = om.MObject()

    #Methode zum Zurückgeben einer Instanz
    @staticmethod
    def creator():
        return sampleArrayNode()

    #Methode, die Maya bei der Initialisierung aufgerufen hat
    #Attribute festlegen
    @staticmethod
    def initialize():
        #Untergeordnete Attribute
        #Koordinate
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.position = nAttr.create(
            'position', 'pos', om.MFnNumericData.k3Float, 0
        )
        nAttr.readable = True
        #Gewicht
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.weight = nAttr.create(
            'weight', 'w', om.MFnNumericData.kFloat, 1
        )
        nAttr.readable = True
        nAttr.setMax(1)  # Min,Max kann auch angegeben werden
        nAttr.setMin(0)

        #Zusammengesetzte Attribute
        nAttr = om.MFnCompoundAttribute()
        sampleArrayNode.input = nAttr.create(
            'input', 'i')
        nAttr.readable = True
        nAttr.array = True
        nAttr.indexMatters = False
        nAttr.addChild(sampleArrayNode.position)
        nAttr.addChild(sampleArrayNode.weight)

        #Die Ausgabe sind diesmal die Koordinaten (3D Float)
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.output = nAttr.create(
            'output', 'o', om.MFnNumericData.k3Float)
        nAttr.storable = True
        nAttr.writable = True
        nAttr.readable = True

        #Führen Sie nach dem Definieren addAttribute von MPxNode aus
        sampleArrayNode.addAttribute(sampleArrayNode.input)
        sampleArrayNode.addAttribute(sampleArrayNode.output)
        #Stellen Sie außerdem den Ausgang so ein, dass er neu berechnet wird, wenn der Eingang geändert wird.
        sampleArrayNode.attributeAffects(
            sampleArrayNode.input, sampleArrayNode.output)

    #Der Konstruktor ruft den übergeordneten Konstruktor auf
    def __init__(self):
        om.MPxNode.__init__(self)

    #Eine Methode, die von Maya aufgerufen wird, wenn der Wert eines Attributs berechnet wird
    def compute(self, plug, dataBlock):
        arrayDataHandle = dataBlock.inputArrayValue(
            sampleArrayNode.input
        )
        sumX = 0
        sumY = 0
        sumZ = 0
        num = len(arrayDataHandle)
        while not arrayDataHandle.isDone():
            #Zusammengesetztes Attributdatenhandle
            dataHandle = arrayDataHandle.inputValue()
            # .Sie können untergeordnete Attribute mit untergeordnetem Element abrufen
            childHandle = dataHandle.child(
                sampleArrayNode.position
            )
            pos = childHandle.asFloat3()
            childHandle = dataHandle.child(
                sampleArrayNode.weight
            )
            w = childHandle.asFloat()
            sumX += pos[0] * w
            sumY += pos[1] * w
            sumZ += pos[2] * w
            arrayDataHandle.next()

        outhandle = dataBlock.outputValue(sampleArrayNode.output)
        if(num != 0):
            outhandle.set3Float(sumX / num, sumY / num, sumZ / num)
        else:
            outhandle.set3Float(0, 0, 0)
        dataBlock.setClean(plug)

    # http://help.autodesk.com/view/MAYAUL/2016/ENU/
    # api1.0 bedeutet MStatus, es sei denn, Sie weisen uns ausdrücklich an, den Stecker nicht zu verarbeiten.kUnknownParameter wird nicht zurückgegeben
    # api2.Bei 0 gibt es überhaupt keinen MStatus, sodass Sie ihn ignorieren können.

#Eine von Maya aufgerufene Funktion, die einen neuen Knoten registriert

Was sich geändert hat, ist, dass die Variablen "Position" und "Gewicht" als Felder der Klasse vorbereitet werden. Da diese als untergeordnete Attribute von zusammengesetzten Attributen verwendet werden, werden sie in der Methode "initialize" genauso wie normale Attribute definiert. Das zusammengesetzte Attribut, das diese zusammenfügt, ist "Eingabe".

#Zusammengesetzte Attribute
        nAttr = om.MFnCompoundAttribute()
        sampleArrayNode.input = nAttr.create(
            'input', 'i')
        nAttr.readable = True
        nAttr.array = True
        nAttr.indexMatters = False
        nAttr.addChild(sampleArrayNode.position) #← Das ist der Punkt
        nAttr.addChild(sampleArrayNode.weight)

Der Unterschied zu normalen Attributen besteht darin, dass die Klasse des Attributs "MFnCompoundAttribute" ist und das oben in ".addChild" definierte untergeordnete Attribut hinzugefügt wird.

So verwenden Sie zusammengesetzte Attribute innerhalb der Berechnungsmethode

            #Zusammengesetztes Attributdatenhandle
            dataHandle = arrayDataHandle.inputValue()
            # .Sie können untergeordnete Attribute mit untergeordnetem Element abrufen
            childHandle = dataHandle.child(
                sampleArrayNode.position
            )
            pos = childHandle.asFloat3()
            childHandle = dataHandle.child(
                sampleArrayNode.weight
            )
            w = childHandle.asFloat()

Verwenden Sie die Methode ".child" aus dem Handle für zusammengesetzte Attributdaten, um das Handle für untergeordnete Attributdaten abzurufen und darauf zuzugreifen.

Tatsächlicher Gebrauch

Wenn Sie den erstellten Knoten tatsächlich verwenden, sieht es so aus. 04.png Verbinden Sie die Positionen mehrerer Objekte mit einem Knoten. Versuchen Sie, den Ausgang mit dem Locator zu verbinden. 03.jpg Der Locator hat sich in die Mitte der Kugel, des Kegels und des Würfels bewegt. Dieses Mal habe ich zusätzlich zu den Koordinaten einen Gewichtungswert als zusammengesetztes Attribut hinzugefügt. Verwenden wir ihn also. 05.jpg Wenn Sie das Gewicht der Kugel senken ... 06.jpg Der Einfluss der Kugel ist verschwunden und der Locator hat sich in die Mitte des Kegels und des Würfels bewegt.

Recommended Posts

[Maya] Schreiben Sie einen benutzerdefinierten Knoten in Open Maya 2.0
Maya | Übergeordnete Knoten nacheinander abrufen
Schreiben Sie DCGAN mit Keras
Schreiben Sie Dekorateur in der Klasse
Mayapy - Python in Maya
Schreiben Sie Python in MySQL
Benutzerdefinierte Sortierung in Python3
So schreiben Sie eine benutzerdefinierte Validierung in Django REST Framework
Schreiben Sie Pandec-Filter in Python
Schreiben Sie die Beta-Distribution in Python
Schreiben Sie Python in Rstudio (reticulate)
Schreiben Sie Spigot in VS Code
Schreiben Sie Daten im HDF-Format
Liste der Knoten in Diagrammen
Schreiben Sie Spinnen-Tests in Scrapy