[PYTHON] [Maya] Ecrire un nœud personnalisé dans Open Maya 2.0

Il est facile d'oublier le flux de travail d'implémentation de nœuds personnalisés dans Maya, je vais donc l'écrire comme rappel.

Cette fois, je présenterai un nœud avec une entrée / sortie simple, un nœud qui peut gérer des tableaux et un nœud qui peut gérer des attributs composés, et enfin l'utiliser pour créer un nœud qui génère les positions centrales de plusieurs points.

Pour les nœuds personnalisés (nœuds de dépendance, nœuds avec entrée / sortie), reportez-vous aux pages officielles suivantes. Bases du plugin Dependency Graph Plugin de graphique de dépendance

Éléments requis pour le script qui définit le nœud de dépendance

Au moins dans le script

  1. Une fonction vide qui vous indique d'utiliser Maya Python API 2.0 Si vous définissez une fonction appelée maya_useNewAPI (), vous pouvez clairement indiquer que l'API 2.0 sera utilisée du côté Maya.

  2. Points d'entrée / sortie du plugin Vous devez appeler ʻinitializePlugin (mobject) et ʻuninitializePlugin (mobject) lors du chargement et de la fermeture du plugin. Ici, nous définissons ** nom de nœud **, ** ID de nœud **, ** les 4/5 fonctions suivantes **, ** type de nœud **, ** classification de nœud ** (voir ci-dessous).

  3. Créez une fonction qui renvoie une instance du nœud

  4. Fonction qui initialise les attributs du nœud

  5. Classe de corps de nœud

Sera nécessaire.

Nœud avec E / S simples

L'exemple de nœud que j'ai réellement écrit est le suivant. C'est un nœud qui multiplie la valeur Float placée dans l'attribut d'entrée par la fonction sin et la sort à partir de l'attribut de sortie. Je vais expliquer un par un.

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

# Maya API 2.Fonctions requises pour utiliser 0
def maya_useNewAPI():
    pass

#Classe réelle
class sampleNode(om.MPxNode):
    id = om.MTypeId(0x7f001) #Identifiant unique https://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
    input = om.MObject()
    output = om.MObject()

    #Méthode pour renvoyer une instance
    @staticmethod
    def creator():
        return sampleNode()

    #Méthode appelée par Maya lors de l'initialisation
    #Définir les attributs
    @staticmethod
    def initialize():
        #Les attributs sont définis à l'aide de la méthode create d'une sous-classe de la classe MFnAttribute.
        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

        #Après avoir défini, exécutez addAttribute de MPxNode
        sampleNode.addAttribute(sampleNode.input)
        sampleNode.addAttribute(sampleNode.output)
        #Définissez également la sortie à recalculer lorsque l'entrée est modifiée.
        sampleNode.attributeAffects( sampleNode.input, sampleNode.output)
        
    #Le constructeur appelle le constructeur parent
    def __init__(self):
        om.MPxNode.__init__(self)

    #Une méthode appelée par Maya lorsque la valeur d'un attribut est calculée
    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 signifie MStatus sauf si vous nous dites explicitement de ne pas traiter la fiche.kUnknownParameter n'est pas renvoyé
    # api2.À 0, il n'y a pas de MStatus en premier lieu, vous pouvez donc l'ignorer.

#Une fonction appelée par Maya qui enregistre un nouveau nœud
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

#Fonctions appelées par Maya lors de la sortie du plug-in
def uninitializePlugin(mobject):
    mplugin = om.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode(sampleNode.id)
    except:
        sys.stderr.write('Faled to uninitialize node: %s' % 'sampleNode')
        raise

Définition de classes pour les nœuds

Premier de la définition de classe

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

Décidez de l'ID du plug-in. Pour plus de détails, consultez site Autodesk, mais vous pouvez généralement utiliser n'importe quelle valeur comprise entre 0x00000 et 0x7ffff. Je vais. Et j'ai préparé les attributs d'entrée et de sortie en tant que champ de classe ʻintput ʻoutput.

Fonction de génération d'instance

    #Méthode pour renvoyer une instance
    @staticmethod
    def creator():
        return sampleNode()

Une fonction qui crée une instance d'un nœud. Vous pouvez l'écrire en dehors de la classe, mais cette fois je l'ai écrit en tant que méthode statique de la classe.

Fonction d'initialisation des attributs

#Méthode appelée par Maya lors de l'initialisation
    #Définir les attributs
    @staticmethod
    def initialize():
        #Les attributs sont définis à l'aide de la méthode create d'une sous-classe de la classe MFnAttribute.
        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

        #Après avoir défini, exécutez addAttribute de MPxNode
        sampleNode.addAttribute(sampleNode.input)
        sampleNode.addAttribute(sampleNode.output)
        #Définissez également la sortie à recalculer lorsque l'entrée est modifiée.
        sampleNode.attributeAffects( sampleNode.input, sampleNode.output)

C'est la partie correspondant à "4. Fonctions qui initialisent les attributs de nœud" dans la liste précédente. Vous pouvez l'écrire en dehors de la classe, mais c'est encombré, donc je l'ai écrit comme une méthode statique. Les attributs sont définis en utilisant les sous-classes appropriées de la classe MFnAttribute. Cette fois, c'est une valeur Float, donc j'utilise MFnNumericAttribute. Les valeurs flottantes tridimensionnelles (coordonnées, etc.) et les valeurs booléennes sont également ce MFnNumericAttribute. Vous pouvez trouver l'angle, la distance et le temps à partir de MFnUnitAttribute, la matrice de MFnMatrixAttribute, et les autres à partir de la référence ci-dessous. MFnAttribute Class Reference OpenMaya.MFnAttribute Class Reference

Spécifiez le nom, l'abréviation, le type et la valeur initiale de l'attribut avec nAttr.create. Si nAttr.storable écrit la valeur de l'attribut dans le fichier de sauvegarde. Il existe d'autres propriétés telles que «inscriptible» et «lisible», alors définissez-les correctement.

sampleNode.addAttribute (sampleNode.input) Ajoute l'attribut créé au nœud. sampleNode.attributeAffects (sampleNode.input, sampleNode.output) Lorsque la valeur de l'attribut d'entrée change, l'attribut de sortie sera mis à jour.

Méthode du corps de calcul

    #Une méthode appelée par Maya lorsque la valeur d'un attribut est calculée
    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)
            

La méthode de calcul est la méthode appelée lorsque le calcul est effectué. Ce nœud calcule Sin. La valeur est transmise sous forme de plug. Pour plus d'informations sur les plugs, accédez à l'aide d'Autodesk (https://help.autodesk.com/view/MAYAUL/2016/JPN/?guid=__files_Dependency_graph_plugins_Attributes_and_plugs_htm). C'est long car il faut un handle du DataBlock pour obtenir l'entrée et l'affecter à la sortie, mais il s'agit en fait de calculer simplement la fonction Sin.

Point d'accès

#Une fonction appelée par Maya qui enregistre un nouveau nœud
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

Un point d'entrée en dehors de la classe. Spécifiez le nom et l'ID du nœud, la méthode d'instance définie dans la classe et le type de nœud.

Courir

Chargez le script que vous avez créé à partir du gestionnaire de plugins de Maya. Créez un nœud avec cmds.createNode ('sampleNode') ou cmds.shadingNode ('sampleNode', asUtility = True) dans la ligne de commande ou l'éditeur de script de Maya. Si vous l'avez créé avec ce dernier, votre propre nœud sera affiché dans l'onglet "Utilitaires" de la fenêtre hypershade. 無題.png

Attributs de tableau

Il existe deux méthodes, l'une consiste à connecter les données du tableau au plug en tant qu'attribut, et l'autre à utiliser le ** plug array ** dans lequel le plug lui-même est un tableau.

Prise de baie

Modifiez la définition d'attribut dans la méthode ʻinitializeplus tôt comme suit.nAttr.array = True est ok, mais vous pouvez utiliser -nextAvailable avec la commande connectAttr en définissant nAttr.indexMatters = False` sur False. Au contraire, s'il vaut True, il semble que l'index à insérer doive être spécifié.

    @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  #ajouter à
        nAttr.indexMatters = False  #ajouter à

Ensuite, la méthode compute qui gère le calcul réel. Cette fois, le processus consiste à sortir la valeur totale du tableau d'entrée.

    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)

Lorsque vous utilisez un plug-in de tableau, récupérez le MArrayDataHandle une fois avec ʻinputArrayValue au lieu de ʻinputValue. Puisqu'il s'agit d'un itérateur, avancez l'itérateur avec next () ou jumpToLogicalElement () et obtenez la valeur de l'élément du tableau avec ʻarrayDataHandle.inputValue () `. Après cela, il est converti en valeur numérique et calculé de la même manière qu'une fiche normale.

01.jpg ↑ La constante 1 + 2 + 3 + 4 = 10 a été calculée correctement.

Attributs composites

Un attribut composite est une collection de plusieurs attributs. Attributs dynamiques complexes Dans cet exemple, les «coordonnées et poids» sont utilisés comme attributs composés, et ils sont utilisés comme connecteurs de tableau.

Cela ressemble à l'image ci-dessous sur l'éditeur de nœuds. 02.png La mise en œuvre de la partie classe est la suivante. Les points d'entrée, etc. sont les mêmes que le code précédent (omis).

class sampleArrayNode(om.MPxNode):
    #Identifiant unique https://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
    id = om.MTypeId(0x7f011)
    input = om.MObject()
    output = om.MObject()
    #Attributs enfants
    position = om.MObject()
    weight = om.MObject()

    #Méthode pour renvoyer une instance
    @staticmethod
    def creator():
        return sampleArrayNode()

    #Méthode appelée par Maya lors de l'initialisation
    #Définir les attributs
    @staticmethod
    def initialize():
        #Attributs enfants
        #Coordonner
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.position = nAttr.create(
            'position', 'pos', om.MFnNumericData.k3Float, 0
        )
        nAttr.readable = True
        #poids
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.weight = nAttr.create(
            'weight', 'w', om.MFnNumericData.kFloat, 1
        )
        nAttr.readable = True
        nAttr.setMax(1)  # Min,Max peut également être spécifié
        nAttr.setMin(0)

        #Attributs composites
        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)

        #La sortie est les coordonnées (3D Float) cette fois
        nAttr = om.MFnNumericAttribute()
        sampleArrayNode.output = nAttr.create(
            'output', 'o', om.MFnNumericData.k3Float)
        nAttr.storable = True
        nAttr.writable = True
        nAttr.readable = True

        #Après avoir défini, exécutez addAttribute de MPxNode
        sampleArrayNode.addAttribute(sampleArrayNode.input)
        sampleArrayNode.addAttribute(sampleArrayNode.output)
        #Définissez également la sortie à recalculer lorsque l'entrée est modifiée.
        sampleArrayNode.attributeAffects(
            sampleArrayNode.input, sampleArrayNode.output)

    #Le constructeur appelle le constructeur parent
    def __init__(self):
        om.MPxNode.__init__(self)

    #Une méthode appelée par Maya lorsque la valeur d'un attribut est calculée
    def compute(self, plug, dataBlock):
        arrayDataHandle = dataBlock.inputArrayValue(
            sampleArrayNode.input
        )
        sumX = 0
        sumY = 0
        sumZ = 0
        num = len(arrayDataHandle)
        while not arrayDataHandle.isDone():
            #Poignée de données d'attribut composite
            dataHandle = arrayDataHandle.inputValue()
            # .Vous pouvez obtenir des attributs enfants avec child
            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 signifie MStatus sauf si vous nous dites explicitement de ne pas traiter la fiche.kUnknownParameter n'est pas renvoyé
    # api2.À 0, il n'y a pas de MStatus en premier lieu, vous pouvez donc l'ignorer.

#Une fonction appelée par Maya qui enregistre un nouveau nœud

Ce qui a changé, c'est que les variables «position» et «poids» sont préparées comme les champs de la classe. Comme ceux-ci sont utilisés comme attributs enfants d'attributs composés, ils sont définis dans la méthode ʻinitialize de la même manière que les attributs normaux. L'attribut composite qui les rassemble est ʻinput.

#Attributs composites
        nAttr = om.MFnCompoundAttribute()
        sampleArrayNode.input = nAttr.create(
            'input', 'i')
        nAttr.readable = True
        nAttr.array = True
        nAttr.indexMatters = False
        nAttr.addChild(sampleArrayNode.position) #← C'est le point
        nAttr.addChild(sampleArrayNode.weight)

La différence avec les attributs normaux est que la classe de l'attribut est «MFnCompoundAttribute» et que l'attribut enfant défini ci-dessus dans «.addChild» est ajouté.

Pour utiliser des attributs composés dans la méthode de calcul

            #Poignée de données d'attribut composite
            dataHandle = arrayDataHandle.inputValue()
            # .Vous pouvez obtenir des attributs enfants avec child
            childHandle = dataHandle.child(
                sampleArrayNode.position
            )
            pos = childHandle.asFloat3()
            childHandle = dataHandle.child(
                sampleArrayNode.weight
            )
            w = childHandle.asFloat()

Utilisez la méthode .child du handle de données d'attribut composite pour obtenir et accéder au handle de données d'attribut enfant.

Utilisation réelle

Lorsque vous utilisez réellement le nœud créé, il ressemble à ceci. 04.png Connectez les positions de plusieurs objets à un nœud. Essayez de connecter la sortie au localisateur. 03.jpg Le localisateur s'est déplacé vers le centre de la sphère, du cône et du cube. Cette fois, j'ai ajouté une valeur de poids en plus des coordonnées en tant qu'attribut composé, alors utilisons-la. 05.jpg Si vous abaissez le poids de la sphère ... 06.jpg L'influence de la sphère a disparu et le localisateur s'est déplacé vers le centre du cône et du cube.

Recommended Posts

[Maya] Ecrire un nœud personnalisé dans Open Maya 2.0
Maya | Obtenir les nœuds parents dans l'ordre
Ecrire DCGAN avec Keras
Écrire un décorateur en classe
mayapy - Python en Maya
Ecrire Python dans MySQL
Tri personnalisé en Python3
Comment écrire une validation personnalisée dans Django REST Framework
Ecrire des filtres Pandec en Python
Écrire une distribution bêta en Python
Ecrire python dans Rstudio (réticulé)
Ecrire Spigot dans VS Code
Écrire des données au format HDF
Liste des nœuds dans les diagrammes
Ecrire des tests Spider dans Scrapy