Modelado de procedimientos con scripts de MEL en maya - Día 2
Spanish (Español) translation by RRGG (you can also view the original English article)



Este tutorial es una introducción al uso de procedimientos y archivos MEL externos, funciones, bucles e interacción con scripts dentro del área de trabajo de Maya. Revisaremos las prácticas básicas de codificación y armaremos un objeto geométrico ingenioso que sería muy difícil de crear sin secuencias de comandos.
Archivos / Complementos Adicionales:
Creando Procedimientos
En este tutorial, crearemos un archivo de script externo con cuatro "procedimientos": scripts autocontenidos destinados a alcanzar objetivos específicos. El primer procedimiento tomará cualquier número de objetos seleccionados en la ventana gráfica Maya y creará una jerarquía encadenada para facilitar las dobles transformaciones. El segundo script anulará los objetos seleccionados, y el tercero moverá los puntos de pivote de los objetos seleccionados al origen del espacio mundial. Estos tres scripts te permitirán crear fácilmente algunos efectos interesantes con múltiples objetos. El cuarto procedimiento combinará los tres primeros para crear una 'Espiral de Fermat', similar al complejo espiral que se encuentra en las cabezas de girasol.
Paso 1
Abierto Maya. Para el curso de este tutorial, usaré Maya 2010, pero los mismos comandos deberían funcionar para la mayoría de las versiones de Maya. Comience creando un cubo en el centro de la ventana gráfica.



Paso 2
Vaya a 'Editar> Duplicar especial' y haga clic en el cuadro de opción para abrir este cuadro de diálogo. Crea nueve copias.



Paso 3
Vaya a 'Ventana> Jerarquía de hipergrafía'. Ahora necesitamos agrupar los cubos para crear una "cadena". Para hacer esto, primero haga clic en 'pCube2', luego haga clic en 'pCube1', y presione 'p' en su teclado. Esto es el principal objeto seleccionado (pCube2) al segundo seleccionado (pCube1).
Continúe este proceso (seleccionando el niño, luego el padre y luego presionando 'p' para encadenarlos) para cada uno de nuestros cubos. Una vez completado, cada objeto en el centro de la jerarquía debe tener un cubo primario y un cubo secundario, y la jerarquía general debe tener este aspecto:



Paso 4
Cierre la ventana 'Jerarquía de Hypergraph'. Seleccione todos los objetos haciendo clic sobre los cubos en el centro de la ventana gráfica. Luego, manteniendo todos los cubos seleccionados, intente moverlos y rotarlos.



Debido a que las transformaciones de cada cubo ahora son relativas al cubo un paso arriba en la cadena, la doble transformación resultante le permite mover los cubos de una manera muy única: las traducciones pequeñas se convierten rápidamente en grandes cambios.
Paso 5
Con todos los cubos aún seleccionados, presione 'Shift-P' en su teclado para quitar los padres de los padres. Ahora hemos recorrido todos los pasos principales que queremos automatizar con nuestro script. Así que comienza un nuevo archivo Maya y abre un editor de texto. Mi favorito personal es Notepad ++, que incluso tiene un complemento de idioma MEL disponible. Siéntase libre de usar el editor de texto que prefiera, pero por ahora usaré el viejo Bloc de notas de Microsoft. Bloc de notas es simple, pero hace el trabajo.



Paso 6
Sigue adelante y guarda tu archivo. Al guardar un documento MEL, asegúrese de elegir "Todos los archivos" en "guardar como tipo". Guarde su documento como un archivo .mel en su directorio de scripts de Maya. Usaré EdW_DoubleTransform.mel como mi nombre de archivo.



Paso 7
Para crear un procedimiento, debes saber un par de cosas de antemano. Primero, debe definir un "alcance" para el procedimiento. El alcance establece desde dónde se puede acceder a un procedimiento. Un alcance "local" significa que el código solo es accesible para otros procedimientos dentro del mismo archivo .mel. "Global" significa que el procedimiento está disponible para otros archivos de comandos y para el usuario, y también se carga en la memoria. En general, utilizo muchos procedimientos globales. Esta no es la mejor práctica de codificación absoluta, pero me gusta tener mis procedimientos disponibles en varios scripts. En segundo lugar, debe elegir un nombre para su procedimiento. En general, intente elegir algo único y descriptivo: si el procedimiento es global, no desea que el nombre entre en conflicto con cualquier otro procedimiento en cualquier otro script. Por lo general, escribo un tipo de iniciales en el nombre, y como el script que estamos escribiendo se llama "DoubleTransform", en este caso usaré "dt". En su forma más básica, este es el procedimiento que estamos construyendo:
1 |
|
2 |
global proc dt_makeParentChain(){ |
3 |
//actions of the script
|
4 |
};
|
Paso 8
Lo primero que haremos con nuestro procedimiento es definir una variable que eventualmente alojará una lista de nombres de objetos. En MEL, las variables se indican con un signo de dólar ($) antes del nombre de la variable. Hay diferentes tipos de datos que una variable puede contener: cadenas de texto, números enteros llamados números enteros, números decimales de coma flotante, valores vectoriales, etc. La sintaxis para declarar una variable es la siguiente:
1 |
|
2 |
dataType $variableName; |
Por ahora, haremos una variable de cadena vacía:
1 |
|
2 |
string $selectedObjects; |
Si queremos, también podemos asignar datos a la variable en la misma línea de código. Las comillas dobles se utilizan para definir cadenas de texto.
1 |
|
2 |
string $selectedObjects = "this is a string of text"; |
un solo signo de igual significa "establecer la variable en". Un doble signo igual significa "es igual a", y se usa cuando se comparan variables.
Paso 9
Para probar nuestra nueva variable, necesitamos agregar un comando de impresión. Esto generará una cadena al Editor de secuencias de comandos cuando se ejecute la secuencia de comandos. En este caso, simplemente estamos imprimiendo el contenido de la variable '$ selectedObjects'.
1 |
|
2 |
print $selectedObjects; |
Paso 10
Añade un comentario a tu nuevo procedimiento. Tiendo a agregar un comentario antes de cada procedimiento, para poder encontrarlos rápidamente más tarde. Algunos de mis scripts tienen docenas de procedimientos, y los comentarios hacen que el proceso de depuración y cambio de código sea mucho más fácil. El código completo ahora debería verse así:
1 |
|
2 |
// Make Parent Chain
|
3 |
global proc dt_makeParentChain(){ |
4 |
string $selectedObjects = "this is a string of text"; |
5 |
print $selectedObjects; |
6 |
};
|
Paso 11
Guarda el archivo y vuelve a Maya. Cada vez que Maya se inicia, escanea el directorio de scripts y carga una lista de todos los archivos allí. Si ha agregado un script al directorio mientras Maya ya está abierto, entonces el sistema no sabe que el archivo existe. En este caso, tiene dos opciones, ya sea reiniciar Maya, o puede usar el comando "rehash" para forzar a Maya a actualizar esa lista de archivos. Usaremos el segundo método, así que vaya a 'Ventana> Editores generales> Editor de secuencias de comandos', escriba lo siguiente:
1 |
|
2 |
rehash; |
Ahora pulsa 'Ctrl-Enter' para ejecutar el comando.
Paso 12
To test out your script, you'll need to "initialize" it in Maya. The "rehash" command tells maya that the file exists, and the "source" command tells Maya to load the script into memory. Type the following into the script editor:
1 |
|
2 |
source EdW_DoubleTransform.mel; |
Luego, escriba el comando para el procedimiento que creamos dentro del script:
1 |
|
2 |
dt_makeParentChain; |
Esto es lo que debe obtener en el editor de secuencias de comandos después de ejecutar el procedimiento:



Paso 13
Ahora que hemos creado un procedimiento de funcionamiento, vamos a averiguar el pseudocódigo para lo que queremos que el procedimiento haga en nuestro script final:
- escribir una lista de objetos seleccionados a una variable
- obten el numero de objetos seleccionados
- padre cada objeto al anterior en la lista
Paso 14
Para obtener una lista de los objetos que se seleccionan en la vista Maya, use el comando list, "ls". La lista se usa generalmente con un indicador que le dice al comando que capture lo que esté seleccionado actualmente en la ventana gráfica de maya, -sl. Intente seleccionar algunos objetos en el viewport maya, y escriba el comando completo (que se muestra a continuación) en el editor de scripts, recuerde presionar 'Ctrl-Enter' para ejecutar el comando.
1 |
|
2 |
ls -sl; |
Paso 15
El comando list genera una serie de cadenas que representan la lista completa de objetos seleccionados. Para que esta información nos sea útil, debemos escribir el resultado del comando "listar" en una variable. Para hacer esto, ajuste el comando de lista en marcas de verificación (Nota, la marca de verificación, también llamada comilla inversa, generalmente se encuentra sobre la tecla de tabulación del teclado ... asegúrese de no usar la marca de comillas simples a continuación a la tecla enter). Envolver un comando en marcas de verificación se traduce como "la salida de". Nuestro comando ahora debería verse así:
1 |
|
2 |
string $selectedObjects = `ls -sl`; |
Paso 16
La variable $ selectedObjects es una sola cadena de texto. Lo que realmente necesitamos es algo que se llama una matriz de cadenas: una lista ordenada de cadenas que están contenidas en una sola variable. Para convertir nuestra cadena en una matriz de cadenas, use corchetes al declarar la variable:
1 |
|
2 |
string $selectedObjects[] = `ls -sl`; |
Ahora, cuando nos referimos a la variable, podemos usar cualquiera de los arreglos:
1 |
|
2 |
$selectedObjects //all the strings in the array |
... O una cadena individual dentro de la matriz, al proporcionar un índice: la posición de la cadena que queremos dentro de la propia matriz. Los valores de índice en MEL siempre comienzan en cero como se muestra a continuación:
1 |
|
2 |
print $selectedObjects[0]; // returns the first string in the array |
3 |
print $selectedObjects[2]; // returns the third string in the array |
Paso 17
También necesitaremos una forma de averiguar cuántos objetos hay en la matriz. Puedes usar el comando "tamaño" para hacer eso. Vamos a crear una nueva variable para almacenar esta información. Un número entero está representado por un número entero, al que siempre se hace referencia en MEL como "int":
1 |
|
2 |
int $numSelectedObjects = `size($selectedObjects)`; |
Paso 18
Ahora necesitamos una forma de parentalizar cada objeto de acuerdo con su lugar en la matriz. Hay varias maneras de hacer esto, pero por ahora usaremos un ciclo básico "para". Los bucles for son comunes en la programación como una forma de realizar una acción varias veces. La sintaxis de un bucle for que se ejecuta diez veces es la siguiente:
1 |
|
2 |
for (int $i = 0; $i < 9; $i++){ //action to be done }; |
La estructura del bucle tiene tres partes. $ i = 0 define una variable entera, $ i
1 |
|
2 |
for($i = 0; $i < $numSelectedObjects; $i++) { |
3 |
//action to be performed
|
4 |
};
|
Paso 19
Ahora necesitamos usar un comando 'padre' para crear la cadena de jerarquía. La sintaxis de este comando es la siguiente:
1 |
|
2 |
parent childObject parentObject; |
Así que en nuestro caso, el comando padre sería:
1 |
|
2 |
for($i = 1; $i < $numSelectedObjects; $i++) { |
3 |
parent $selectedObjects[(i-1)] $selectedObjects[i]; |
4 |
};
|
Paso 20
Con nuestro ciclo completado, ahora debemos deseleccionar el último objeto de la selección, usando el comando "seleccionar" con la bandera "deseleccionar". Para calcular el índice del último objeto seleccionado, tomamos el número total de objetos y luego, como el índice de cualquier matriz en Maya comienza en 0, reste 1.
1 |
|
2 |
select -deselect $selectedObjects[($numSelectedObjects - 1)]; |
Paso 21
Y con eso, nuestro primer procedimiento ya está completo:
1 |
|
2 |
//Make Parent Chain
|
3 |
global proc dt_makeParentChain(){ |
4 |
string $selectedObjects[] = `ls -sl`; |
5 |
int $numSelectedObjects = `size($selectedObjects)`; |
6 |
for($i = 1; $i < $numSelectedObjects; $i++) { |
7 |
parent $selectedObjects[($i - 1)] $selectedObjects[$i]; |
8 |
};
|
9 |
select -deselect $selectedObjects[($numSelectedObjects - 1)]; |
10 |
};
|
Paso 22
Debajo de 'dt_makeParentChain', cree un nuevo procedimiento para separar los objetos seleccionados. El código debajo de los padres selecciona los objetos seleccionados para el mundo, que es lo mismo que eliminarlos.
1 |
|
2 |
global proc dt_unParentSelected(){ |
3 |
parent -w; |
4 |
};
|
Paso 23
Nuestro tercer procedimiento automatizará el proceso de mover el punto de pivote de un objeto al origen. Al igual que antes, comenzamos creando el esquema del procedimiento:
1 |
|
2 |
global proc dt_pivotToOrigin(){ |
3 |
//procedure actions
|
4 |
};
|
Paso 24
Luego usamos la misma técnica que en el primer procedimiento para escribir una lista de los objetos seleccionados en una variable:
1 |
|
2 |
string $selectedObjects[] = `ls -sl`; |
Paso 25
Ahora tenemos que añadir en un bucle. Si bien podríamos usar el mismo tipo de bucle for que en el primer script, usemos un bucle para cada bucle. Un bucle "para cada" es una versión especializada del bucle for que se ejecuta una vez para cada valor en una matriz. También le permite escribir una variable que representa el valor del índice actual. La sintaxis se ve así:
1 |
|
2 |
for( $each in $arrayVariable ){ |
3 |
//do this action with $each
|
4 |
};
|
La variable $ cada contiene el valor del índice actual de la matriz. Para nuestro script, este es el aspecto que debe tener el código:
1 |
|
2 |
for($thisObj in $selectedObjects){ |
3 |
//move the pivot of $thisObj
|
4 |
};
|
Esta misma técnica se puede utilizar para automatizar cualquier comando maya para cualquier número de objetos seleccionados.
Paso 26
Usando la variable $ thisObj ahora podemos seleccionar el objeto que actualmente está siendo observado por el bucle:
1 |
|
2 |
select -r $thisObj; |
Paso 27
Para mover el pivote al origen, podemos usar el comando "xform" y restablecer los indicadores -rp (rotatePivot) y -sp (scalePivot) a 0 en los ejes X, Y y Z:
1 |
|
2 |
xform -ws -rp 0 0 0 -sp 0 0 0; |
Paso 28
Los componentes principales de la secuencia de comandos ahora están terminados, y su código, que contiene los tres procedimientos, debe tener este aspecto:
1 |
|
2 |
//Make Parent Chain
|
3 |
global proc dt_makeParentChain(){ |
4 |
string $selectedObjects[] = `ls -sl`; |
5 |
int $numSelectedObjects = `size($selectedObjects)`; |
6 |
for($i = 1; $i < $numSelectedObjects; $i++) { |
7 |
parent $selectedObjects[($i - 1)] $selectedObjects[$i]; |
8 |
};
|
9 |
select -deselect $selectedObjects[($numSelectedObjects - 1)]; |
10 |
};
|
11 |
|
12 |
// Un-Parent Objects
|
13 |
global proc dt_unParentSelected(){ |
14 |
parent -w; |
15 |
};
|
16 |
|
17 |
// Move Pivot to Origin
|
18 |
global proc dt_pivotToOrigin(){ |
19 |
string $selectedObjects[] = `ls -sl`; |
20 |
for($thisObj in $selectedObjects){ |
21 |
select -r $thisObj; |
22 |
xform -ws -rp 0 0 0 -sp 0 0 0; |
23 |
};
|
24 |
};
|
Paso 29
Ahora vamos a crear un cuarto procedimiento que usará los tres anteriores para crear una interesante progresión geométrica llamada "Espiral de Fermat", que se basa en la relación angular de la relación dorada, phi. Es una forma que se encuentra comúnmente en la naturaleza y sigue todo tipo de reglas geométricas interesantes. Comencemos escribiendo el pseudocódigo:
- crea una esfera
- crea 1000 copias
- cadena de padres todas las esferas
- mover los objetos en el eje x
- Mueve todos los puntos de pivote de la esfera al origen.
- gire las esferas en Y 137.5 grados (el ángulo dorado)
Paso 30
Entonces, para empezar, vamos a crear un nuevo esquema de procedimiento:
1 |
|
2 |
global proc dt_fermatSpiral(){ |
3 |
|
4 |
}
|
Paso 31
Ahora vamos a crear una esfera con 8 divisiones radiales y 8 de altura, y escribiremos los nodos de transformación y forma resultantes en una matriz de cadenas para que podamos hacer un seguimiento de ellos. La mayoría de los comandos que crean geometría generan dos cadenas: el nombre del nodo de transformación y el nombre del nodo de forma. En su mayor parte, desea trabajar con la cadena del nodo de transformación, que se almacenará en el índice [0] en la variable de matriz $ seedSphere creada a continuación:
1 |
|
2 |
string $seedSphere[] = `polySphere -sx 8 -sy 8`; |
Paso 32
Ahora vamos a crear una variable de matriz de cadena vacía:
1 |
|
2 |
string $allSpheres[]; |
Paso 33
Luego abre un for-loop que se ejecuta 1,000 veces:
1 |
|
2 |
for ($i=1; $i<1000; $i++){ |
Paso 34
Ahora necesitamos duplicar nuestra esfera recién creada y escribir los nodos de transformación y forma resultantes en una variable:
1 |
|
2 |
string $duplicatedSphere[] = `duplicate`; |
Paso 35
Aquí, agregamos el nombre de nuestro nodo de transformación duplicado al final de la matriz $ allSpheres. Puede usar el comando "tamaño" para determinar cuántos objetos ya están en la matriz y usar el resultado como el índice para nuestro nuevo valor.
1 |
|
2 |
$allSpheres[size($allSpheres)] = $duplicatedSphere[0]; |
Paso 36
Aquí seleccionamos todas las esferas creadas y ejecutamos el comando de cadena principal que creamos anteriormente:
1 |
|
2 |
select -r $allSpheres; |
3 |
dt_makeParentChain; |
Paso 37
Después de criar las esferas juntas, las seleccionamos de nuevo y las movemos todas .05 en X usando el comando "mover":
1 |
|
2 |
select -r $allSpheres; |
3 |
move 0.05 0 0; |
Paso 38
Después de restablecer la posición de origen utilizando nuestro procedimiento "pivotToOrigin" creado previamente, seleccionamos las esferas una última vez y las giramos 137.5 grados en Y. Con eso hecho, ejecutamos nuestro script sin padre:
1 |
|
2 |
dt_pivotToOrigin; |
3 |
select -r $allSpheres; |
4 |
rotate 0 137.5 0; |
5 |
dt_unParentSelected; |
Paso 39
Y eso completa nuestro procedimiento final. Entonces, nuestro script completo debería verse así:
1 |
|
2 |
//Make Parent Chain
|
3 |
global proc dt_makeParentChain(){ |
4 |
string $selectedObjects[] = `ls -sl`; |
5 |
int $numSelectedObjects = `size($selectedObjects)`; |
6 |
for($i = 1; $i < $numSelectedObjects; $i++) { |
7 |
parent $selectedObjects[($i - 1)] $selectedObjects[$i]; |
8 |
};
|
9 |
select -deselect $selectedObjects[($numSelectedObjects - 1)]; |
10 |
};
|
11 |
|
12 |
// Un-Parent Objects
|
13 |
global proc dt_unParentSelected(){ |
14 |
parent -w; |
15 |
};
|
16 |
|
17 |
// Move Pivot to Origin
|
18 |
global proc dt_pivotToOrigin(){ |
19 |
string $selectedObjects[] = `ls -sl`; |
20 |
for($thisObj in $selectedObjects){ |
21 |
select -r $thisObj; |
22 |
xform -ws -rotatePivot 0 0 0 -scalePivot 0 0 0; |
23 |
};
|
24 |
};
|
25 |
|
26 |
//Create Fermat's Spiral of Spheres
|
27 |
global proc dt_fermatSpiral(){ |
28 |
string $seedSphere[] = `polySphere -sx 8 -sy 8`; |
29 |
string $allSpheres[]; |
30 |
for ($i=1; $i<1000; $i++){ |
31 |
string $duplicatedSphere[] = `duplicate`; |
32 |
$allSpheres[size($allSpheres)] = $duplicatedSphere[0]; |
33 |
};
|
34 |
select -r $allSpheres; |
35 |
dt_makeParentChain; |
36 |
select -r $allSpheres; |
37 |
move 0.05 0 0; |
38 |
dt_pivotToOrigin; |
39 |
select -r $allSpheres; |
40 |
rotate 0 137.5 0; |
41 |
dt_unParentSelected; |
42 |
};
|
Paso 40
Y con eso hemos terminado! Guarde el script y vuelva a ejecutar el proceso 'rehash' / 'source' como se detalla arriba. Luego ingrese el siguiente comando en el editor de secuencias de comandos de Maya, presione 'Ctrl-Enter' y espere unos segundos:
1 |
|
2 |
dt_fermatSpiral(); |
Y ahí tienes la Espiral de Fermat, construida completamente usando MEL. Intenta jugar con diferentes ángulos, formas y transformaciones en el procedimiento final. ¡Puedes obtener todo tipo de patrones interesantes!


