Go to English version

Os presento mi primer plugin para Adobe Substance 3D Designer: “Print Modified Values”. Va a ser un post largo, así que lo voy a dividir en varias secciones:

  • Cómo instalarlo
  • Cómo usarlo
  • Algunos comentarios y limitaciones conocidas
  • Breve diario de producción – Los problemas que han ido surgiendo y cómo se han resuelto

El propósito de este plugin es el de añadir un Comment como “hijo” del nodo que tengamos seleccionado y que contenga la información de aquellos parámetros que han sido modificados.

En febrero de 2023 voy a dar un curso de Designer en mi ciudad (pronto compartiré más información) y en la preparación de los contenidos para ese evento me di cuenta de lo útil que sería esta herramienta. Busqué y pregunté, pero no encontré nada, así que me puse yo mismo manos a la obra.

Después de bastante trabajo, aquí tenéis la primera versión. Espero que os sea útil, especialmente a aquellos que os gusta divulgar compartiendo capturas de vuestros Graphs. A mí desde luego me está ahorrando mucho trabajo, espero que más del que me ha tomado crearlo… 🤪 En cualquier caso he aprendido mucho, no solo sobre API de Designer, sino también acerca de la propia estructura interna de la aplicación.


⬇️ Bajar “Print Modified Values”

AVISO LEGAL

Versión corta: He hecho todo lo posible para crear una herramienta estable y robusta. Pero úsala con precaución, por favor. Recomiendo crear una copia de tu archivo antes de proceder a comentar sus nodos utilizando este plugin.

Versión larga: EL SOFTWARE SE PROPORCIONA “TAL CUAL”, SIN GARANTÍA DE NINGÚN TIPO, EXPRESA O IMPLÍCITA, INCLUIDAS, ENTRE OTRAS, LAS GARANTÍAS DE COMERCIABILIDAD, IDONEIDAD PARA UN FIN DETERMINADO Y NO INFRACCIÓN. EN NINGÚN CASO LOS AUTORES O LOS TITULARES DE LOS DERECHOS DE AUTOR SERÁN RESPONSABLES DE NINGUNA RECLAMACIÓN, DAÑO U OTRA RESPONSABILIDAD, YA SEA EN UNA ACCIÓN CONTRACTUAL, EXTRACONTRACTUAL O DE OTRO TIPO, QUE SURJA DE, O ESTÉ RELACIONADA CON EL SOFTWARE O EL USO U OTRAS OPERACIONES CON EL SOFTWARE.


Cómo instalarlo

Una vez bajado y descomprimido el ZIP, trasladad la carpeta principal “print_modified_values” (que a su vez contiene un fichero .json y otra subcarpeta con el mismo nombre) a donde normalmente tengáis instalados otros plugins de Designer.

Si nunca lo habéis hecho, os recomiendo usar el siguiente directorio:

/Users/NOMBRE/Documents/Adobe/Adobe Substance 3D Designer/python/sduserplugins/

Luego arrancáis Designer, abrís Preferencias / Projects, pincháis en el tab vertical “User project” (dentro de Configuration / Project Files) y luego en el tab horizontal Python. Pincháis en el iconito + de la derecha y añadís el directorio anterior. Y tal como indica en amarillo, una vez hecho esto tendréis que reiniciar Designer.

Una vez reiniciado Designer ya debería estar cargado el plugin. Pero por si acaso abrís Tools / Plugin Manager y comprobáis que el checkbox de nuestro plugin está marcado. De esta manera, por cierto, podéis activar o desactivar cualquier plugin.

Plugin Manager

Nada más instalarlo todavía no lo veréis aparecer en vuestra barra superior. Para ello es necesario que primero creéis un nuevo Substance Graph. Hacedlo. Ahora veréis aparecer 2 nuevos botoncitos en la barra de herramientas: el que no tiene color puede activar o desactivar la presencia del otro, en naranja.

Cómo usarlo

Es muy sencillo: supongamos que tenemos un nodo Splatter Circular que hemos modificado y queremos anotar qué parámetros hemos cambiado. Simplemente lo seleccionamos y pulsamos el botoncito naranja. Automáticamente se nos añadirá un nodo Comment como “hijo” de nuestro Splatter (“hijo” significa que al mover el nodo, el comentario se traslada con él)

También podéis usar “Q” como atajo de teclado en lugar del botón, pues es mucho más ágil. He elegido esa letra porque evoca a “Query” (preguntar al nodo sobre sus propios parámetros). Si no os gusta ese atajo o entra en conflicto con otro que ya teníais, lo podéis cambiar editando el archivo __init__.py situado dentro de la subcarpeta “print_modified_values”. Pero cuidado no toquéis ninguna otra cosa, si no sabéis lo que estáis haciendo 😉

Algunos comentarios y limitaciones conocidas

De uno en uno

Solo se pueden añadir Comments de uno en uno, no seleccionéis varios nodos de golpe. No sería muy difícil cambiar el código para hacer que comente varios nodos de vez, pero de momento prefiero mantenerlo así, por varias razones. Entre otras, porque si con un nodo se atasca, se pararía todo el proceso. También he comprobado que conforme añadimos los comentarios tenemos que ir moviendo los nodos para ir haciendo un poco más de espacio para que no se solapen, así que conviene ir uno por uno. Además, al seleccionar varios nodos, si nos pasamos de cantidad, podríamos empezar a “sobrecargar” el programa.

“All by default”

Cuando un nodo no ha sido modificado se añade igualmente un Comment indicando “All by default”. Aunque luego acabe eliminándolo, en principio prefiero que funcione así, porque de esa forma estoy más seguro de que lo ha procesado correctamente. Si no añadiera ningún Comment, no podría estar seguro de si es porque está todo por defecto o porque ha habido algún error interno. Cuando el plugin encuentra un problema, se detiene, dando un error y no añade nada. Eso solo puede verse si abrimos la consola. También he comprobado que prefiero ver un “All by default” que no ver nada, porque de lo contrario no estaría seguro de si quizás olvidé comentar ese nodo…

¿Por qué a veces no hace nada?

En algunos nodos (SVG, Bitmap, FX-Map, Inputs… y seguro que algún otro) veréis que se añade el comentario “Non supported” o incluso directamente no hace nada. En realidad sí hace, o intenta hacer, y lo que ocurre es que nos da un error, como podéis comprobar en la consola. Existen muchos posibles tipos de estructuras de datos. De momento cubre todos los que están dentro de estos grupos: Enumerators, Arrays, Int, Int2, Float, Float2, Float3, Float4, ColorRGBA, Bool, String y Texture. Pero seguro que me he dejado alguna tipología… 

Por ejemplo, cuando el API nos entrega la info de un cierto valor de Position Random lo hace de esta forma:

('Position Random', 'SDValueFloat2(float2(0.17365,0.3249))')

Y el plugin se encarga de convertir esto a un mucho más legible y redondeado:

Position Random 0.17 0.32

Y es muy probable que para cierta Propiedad “x” de cierto Nodo “y” se use una tipología de dato que no tenga contemplada en mi código. O quizás el error es por algún otro motivo, vete a saber…

De todas formas, tras utilizarlo unos cuantos días en muchos graphs, os puedo decir que funciona de maravilla el 99% de las veces y no me ha causado ni un solo problema de cuelgue o desestabilización de la aplicación.

El atajo “Q” deja de funcionar

Lo que sí he visto muy de vez en cuando, es que de repente deja de funcionar el atajo de teclado (aunque sí sigue haciéndolo el botón). Como digo es muy de ciento a viento, y no he conseguido averiguar por qué ocurre, una cosa muy rara. Una forma de recuperarlo sin necesidad de reiniciar la aplicación es abrir el Plugin Manager, desactivar y volver a activar el plugin. Y entonces seleccionar o crear un nuevo Graph para que se cargue de nuevo. Entonces volverá a funcionar el atajo de teclado.

Breve diario de producción

La creación de este plugin ha sido una pequeña odisea. Para empezar yo no soy programador, ni siquiera me considero un verdadero aficionado. Mi única experiencia con código ha sido creando algunos scripts en Python para Modo y ya hace bastantes años de eso… Así que desde el principio me tropecé con multitud de problemas, la mayor parte debidos a mi total inexperiencia con el API de Designer, pero también alguno debido a ciertas limitaciones del mismo y por supuesto a mi pobre experiencia con el propio Python (no consigo hacerme con los conceptos de “Classes” y el constante uso de “Self”).

Por si a alguno os interesa la parte más técnica de estos asuntos aquí os dejo con una especie de “diario de producción”.

Problema 1

Inicialmente yo pretendía hacer un query al nodo seleccionado para saber cuáles eran sus parámetros por defecto y agregarlos a un diccionario. Luego averiguar los parámetros actuales —algunos modificados y otros no—, crear un segundo diccionario, para terminar comparando ambos diccionarios y ver dónde estaban las diferencias (es decir, solo los parámetros modificados). Buscando en la documentación de la API de Designer veo que existe un getDefaultValue() así que intento usarlo, pero por muchas vueltas que le doy, no consigo que me entregue esos valores por defecto. O bien obtengo mensajes de error o bien resultados “none” (!?)

Así que voy al Discord de Substance Designer donde sé que hay buen ambiente, soporte e incluso puedes conversar con el personal de Adobe. Tras unas cuantas vueltas, un empleado de Adobe con alias est me confirma lo que temía:

Mirando nuestro código, parece que los valores por defecto de las propiedades no están correctamente implementados para algunos tipos de gráficos. He creado un ticket en nuestro issue tracker para arreglar esto en el futuro. Una alternativa no muy agradable por ahora podría ser crear un nuevo nodo, obtener los valores de las propiedades y luego eliminar el nodo.

¡Vaya por Dios, la primera en la frente! Ya es mala suerte, mi primer plugin y empezamos con problemas…

Así que para continuar, de momento decido añadir un segundo nodo “virgen” para usarlo de referencia. Lógicamente el objetivo es hacerlo en el propio código, pero primero vamos a ver si funciona añadiéndolo a mano.

Problema 2

La idea ahora es seleccionar primero el nodo virgen, luego el nodo modificado y crear los dos diccionarios, para después compararlos. Voy avanzando, pero pronto me doy cuenta de que a veces me da la información del “nodo equivocado”. El problema estriba en una nueva limitación del programa que ya intuyo y el bueno de est me vuelve a confirmar:

“Las selecciones en Designer no están ordenadas. No hay garantías sobre el orden de los elementos seleccionados.”

Es decir, que aunque yo seleccione primero el nodo virgen y luego el modificado, Designer no retiene esa información. Simplemente crea un array con los nodos seleccionados, pero sin saber en qué orden. Así que no hay forma de estar seguros de cuál es el virgen y cual el modificado.

Esto es un fastidio, pero sigo avanzando, pues cuando aprenda a añadir el nodo de referencia automáticamente quiero creer que podré solventarlo. Gracias a la ayuda de otro empleado de Adobe, Luca Giarrizzo, consigo convertir lo que hasta ese momento no era más que código básico pegado en el Python Editor de Designer en un verdadero plugin.

Problema 3

Una vez tengo armado el plugin, consigo resolver algunas otras cosas (como la correcta ordenación de los parámetros, para que aparezcan igual que en el nodo) y me centro en intentar añadir el nodo de referencia internamente, a través de código. Y de nuevo me enfrento con un obstáculo.

Enseguida aprendo que añadir un Atomic Node es tan simple como:

sdSBSCompGraph.newNode('sbs::compositing::blend') — Funciona

Sin embargo, si intento hacer lo mismo con un Instance Node:

sdSBSCompGraph.newNode('sbs::compositing::shape') — No funciona

Otra vez viene el bueno de est al rescate para confirmarme que no es tan sencillo:

“El contenido de la Library o los Graphs del Explorer que se quieren instanciar en un gráfico no son nodos, son instancias de nodos (de un gráfico). El proceso para instanciar un gráfico en otro gráfico es un poco enrevesado hoy en día. Básicamente necesitas abrir el package que contiene el Graph que quieres instanciar, obtener un SDPackage, encontrar el SDGraph que se quiere instanciar y llamar a SDGraph.newInstanceNode(sdGraphToInstance). Nos gustaría facilitar este proceso en una futura versión de Designer.”

Tras darle vueltas y ver algún tutorial consigo averiguar cómo añadir un Instance Node. Por ejemplo, para un Shape sería:

resource_dir = sd_application.getPath(SDApplicationPath.DefaultResourcesDir)
file_path = os.path.join(resource_dir, 'packages', 'pattern_shape.sbs')
package = sd_pkg_mgr.loadUserPackage(file_path)
resource = package.findResourceFromUrl('shape')
graph.newInstanceNode(resource)

¡Genial, ya sé cómo añadir cualquier tipo de nodo!

Problema 4

Pero, un momento, todavía queda una cuestión importante: solo sé cómo añadir un nodo si de antemano conozco si es Atomic o Instance, y también si tengo dos datos: su Package y su Graph Instance.

Así que lo primero es averiguar toda esa info del nodo (modificado) que tengamos seleccionado, para poder añadir otro igual, pero virgen. Le dedico bastante tiempo al tema… y no consigo averiguarlo. Vuelvo a preguntarlo en el canal de Discord… por dos veces… y no obtengo ninguna respuesta.

Me paso un par de días estudiando el vídeo tutorial “Intro to Plugin Creation in Substance Designer” de uno de los mayores cracks de Designer, Ben Wilson, y buscando información por mi cuenta… pero finalmente no consigo averiguarlo.

Además, también detecto un nuevo problema: cuando añado un Instance Node usando el código que he pegado arriba (aprendido del tutorial de Ben) detecto que además de añadirse el nodo a mi Graph actual también se abre el Package original del nodo en el Explorer. Lo cual significa que también tendré que aprender a cerrarlo (cosa que parece obvia pero no logro encontrar).

Y todavía aparecen más problemas, no tan graves, pero no quiero alargar esto demasiado. Así que ya cansado, tiro la toalla. No puedo dedicarle más tiempo a esto…

Como ya había enseñado en Twitter algunas capturas del plugin en acción, aún con esa limitación de tener que añadir un nodo extra virgen para usarlo de referencia, decido compartirlo tal como lo tengo. E incluso llego a escribir este post explicando todos los problemas y pidiendo ayuda para intentar mejorarlo. Eso fue el domingo 13 de noviembre por la tarde.

¡Y de repente se hizo la luz!

Pero llega el lunes, y cuando ya había decidido compartirlo de esa forma, como digo, de repente un tal Divyansh, otro usuario de Discord, me responde y me da una buena pista de cómo seguir. Por fin aprendo cómo obtener el Package y el Graph Instance del nodo seleccionado.

Enseguida, ya por privado, el bueno de Luca Giarrizzo también contesta a todas mis dudas, y poco a poco voy viendo cómo resolver los problemas: saber si el nodo que tengo seleccionado es Atomic o Instance; saber como seguir operando con los dos nodos, el original modificado y la referencia virgen, sin necesidad de seleccionarlos; yo mismo descubro cómo eliminar el package que se abre automáticamente y cómo eliminar el propio nodo de referencia una vez obtenido su diccionario…

Poco a poco voy puliendo muchas cosas, especialmente cómo conseguir que en lugar de recibir un número entero “3” cuando tenemos seleccionado cierto parámetro de un drop menú, extraer la información de que eso corresponde realmente a un modo “Multiply”, gracias al uso de los Enumerators, lidiar con ciertas inconsistencias en la asignación de enteros en esos mismos Enumerators, saber cómo crear diccionarios sobre la marcha, “en vivo”, interrogando a distintas Properties del nodo, conseguir adaptar la información que obtenemos de Levels si este actúa en modo Grayscale o Color,  etc, etc

Así que finalmente, tras mucho trabajo y rodeos (no queráis saber la cantidad de horas y días que he dedicado a esto…) consigo hacer que el plugin funcione exactamente como yo quería. Solo queda probarlo durante unos días más, preparando esos materiales formativos de los que os hablaba al principio, y ya por fin compartirlo con el resto de usuarios. Porque una cosa así no me apetece guardarla para mí solo, ni tampoco pedir dinero por ella. Hay mucha gente generosa que comparte de forma altruista cantidad de información y recursos de muchísima utilidad, sin pedir nada a cambio. Y eso es algo de lo que me gusta participar.

Así que finalmente espero que vosotros también lo encontréis tan útil como a mí me lo parece 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.