Tools for Cycles PyNodes

I’ll take a look at the code… 2.79b was a python jump inside the api, some bugs might be present, so a more detailed testing is needed.

ps.: Collaboration added :wink:

thats really amazing guys ., how did you make that 'add image texture slot in py node … i mean i convert it but i did not get that add image bar in the which i created

The convertion just rebuilds the nodegroup into python code, it’s mainly to help storing a big nodegroup into the python module… It doesn’t generate custom functions that deal with the node_tree data, nor the draw functions (just input and output sockets are drawned automatically). All functionality that a normal nodegroup doesn’t have must be coded by hand.

In case of sirmaxim’s node, the ‘file opener’ is done by having a string property with subtype=‘FILE_PATH’, and exposing that property in the draw function with layout.prop(self, property, text=‘prop title’).

Yeah, so currently I’ve run into a number of edge cases when testing that I’m working out. One of the things I’ve run across is that I can’t seem to do
self.node_tree.nodes['ShaderNodeTexImage'].image = None
so it looks like I have to remove and recreate nodes on the fly if switching textures, or it will leave any that aren’t directly replaced, so it’s going to require a near rewrite. I wasn’t expecting to have to go that far, but it is what it is.

I thought about dynamically adding/removing output sockets as well, but that could be pretty inconvenient if you’re just trying several texture sets “oh, that’s too much rust, let me try a different set” sort of thing. You’d want to just leave the links in place.

I’m also thinking about what all textures to include. The current setup has all the most common, but doesn’t account for things like transparency, clearcoat normals, emission… How far to go? At some point that becomes a lot of edge cases to account for.

So, I over-estimated how difficult that rewrite would be :smiley:

Simplifying the syntax really helps make this strait forward.

github link: https://github.com/sirmaxim/ShaderNodesExtra/blob/textureset/Nodes/ShaderNodeTextures.py

Hi @sirmaxim,

I try to understand how you add file path and use it in group. So I take your code on github and simplify it.

I try :

import bpy
import os
from glob import glob
from os import path
import re
from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty, StringProperty, FloatVectorProperty, CollectionProperty
from bpy_extras.io_utils import ImportHelper, ExportHelper
from ShaderNodeBase import ShaderNodeBase

class Blur2(ShaderNodeBase):

bl_name='Test'
bl_label='Test'
bl_icon='NONE'

def updateFilePath(self, context):
    self.addNode('ShaderNodeTexImage', {'name':'Base Color'})
    self.addLink('nodes["Base Color"].outputs[0]', 'nodes["Group Output"].inputs[0]')
    bpy.data.images.load(filepath, check_existing=True)
    self.node_tree.nodes['Base Color'].image = bpy.data.images[filepath]

filepath = StringProperty(name="Maps DIR", description="image path", subtype="FILE_PATH", update=updateFilePath)

def defaultNodeTree(self):
    self.addOutput('NodeSocketColor', {'name':'Out', 'default_value':[0.000,0.000,0.000,0.000]})

def init(self, context):
    self.width = 220
    self.setupTree()

def free(self):
    if self.node_tree.users==1:
        bpy.data.node_groups.remove(self.node_tree, do_unlink=True)

def draw_buttons(self, context, layout):
    col=layout.column()
    col.prop(self, 'filepath', text="Base Color")

def draw_label(self):
    node_label = path.basename(self.filepath)
    return node_label

def draw_menu():
    return 'SH_NEW_TexTools' , 'Texture Group'

I’m completly beginer in Blender API and obviously this noob code doesn’t work :sweat_smile:

Thanks a lot for your help @sirmaxim, @secrop and everybody.

self.filepath everywhere you reference it, firstly. That’s just a python thing. You either have to put a variable like that before anything that would use it (works), or refer to it by its proper path (better). Python is OOP so you have to tell it where to find that variable, in this case self because it is a variable of the class.

You may also face issues with blender’s relative paths, which is why I had to do:

def updateFilePath(self, context):
        selectedfile = bpy.path.basename(self.filepath)
        directory = os.path.dirname(bpy.path.abspath(self.filepath))

There’s a little trickery there with the difference between os.path and bpy.path.

In the future, it helps a lot if you can provide the console error output when asking someone to look at your code. :wink:

@sirmaxim thanks to take a time to answer to me. I code in other OOP languages but it’s only since few day I try to code something in Blender. And the start is a nightmare :grinning:

Excuse I didn’t go to see console error because the script runned, just my output was pink color, like when the image’s path missed.

Now it works ! Isolate this part of your code and understand it is very powerfull.

Again thanks a lot.

I share this little code, if it can help someone :

import bpy
import os
from glob import glob
from os import path
import re
from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty, StringProperty, FloatVectorProperty, CollectionProperty
from bpy_extras.io_utils import ImportHelper, ExportHelper
from ShaderNodeBase import ShaderNodeBase

class Blur2(ShaderNodeBase):

bl_name='Blur2'
bl_label='Blur2'
bl_icon='NONE'


def updateFilePath(self, context):
    self.addNode('ShaderNodeTexImage', {'name':'Base Color'})
    self.addLink('nodes["Base Color"].outputs[0]', 'nodes["Group Output"].inputs[0]')
    selectedfile = bpy.path.basename(self.filepath)
    # directory = os.path.dirname(bpy.path.abspath(self.filepath))
    bpy.data.images.load(self.filepath, check_existing=True)
    self.node_tree.nodes['Base Color'].image = bpy.data.images[selectedfile]

filepath = StringProperty(name="Maps DIR", description="image path", subtype="FILE_PATH", update=updateFilePath)

def defaultNodeTree(self):
    self.addOutput('NodeSocketColor', {'name':'Out', 'default_value':[0.000,0.000,0.000,0.000]})

def init(self, context):
    self.width = 220
    self.setupTree()

# def copy(self, node):
#     self.node_tree=node.node_tree.copy()

def free(self):
    if self.node_tree.users==1:
        bpy.data.node_groups.remove(self.node_tree, do_unlink=True)

#def socket_value_update(self, context):

#def update(self):

def draw_buttons(self, context, layout):
    col=layout.column()
    col.prop(self, 'filepath', text="Base Color")

#def draw_buttons_ext(self, contex, layout):

def draw_label(self):
    node_label = path.basename(self.filepath)
    return node_label

def draw_menu():
    return 'SH_NEW_TexTools' , 'Texture Group'

Hi guys! I was trying to explore the possibilities of nodes groups and beyond, saw that you can do your own custom pynodes and then found this!

It looks great! :slight_smile: Unfortunately I am not a coder… trying to convince my self to seriously learning but still didn’t succeed to convince my self.

Doing what @sirmaxim wanted and succeeded in doing, was exactly the reason why I started looking into this in the first place! well done! :slight_smile:

How can I add the node using the code that you kindly provided? I tried copying it into the text editor and run script but I couldn’t find any new node.

I also tried using @Secrop addon, but I get this error everytime I tried to launch the “convert selected node group to pynode” command:

Traceback (most recent call last):
  ************************\AppData\Roaming\Blender Foundation\Blender\2.79\scripts\addons\ShaderNodesExtra-master\__init__.py", line 180, in poll
    currentNode = context.active_node
AttributeError: 'Context' object has no attribute 'active_node'

location: <unknown location>:-1

Thank you for sharing the addon and the codes for the nodes! :slight_smile:

@Bernardo, the converter operator must be called from the node_editor (your mouse cursor must be in the node editor)… Select the nodegroup, press SPACE, and look for “convert selected nodegroup to pynode”. It should be working in the vanilla versions from 2.79.
If you want I can convert your nodegroup for you; or even make it as a standalone, if you prefer… just send me a file with the nodegroup.

I have a preliminary version of it coming up for 2.8 (plus some pending patches for the GLSL Shading), but because Jacques Lucke is doing some changes/fixes in the nodes api (he started this week at BF), I’m waiting for his changes to review my addon.

Hi Secrop, thank you for your answer!

Tomorrow I’ll try again and I’ll let you know how it goes! :slight_smile: Thanks for the offer but for now I’d like to try to learn to use this on my own! :slight_smile:

Hi! I tried again.

unfortunately when I try to look for the command inside the node editor, it just doesn’t show up. You can see in these 2 images that from the viewport I can find the command, but then I get the error. If I look from the node editor the command is not there.

Do you also have some hint on how to proceed to convert the code into a node?

the operator only shows up if the active node is a nodegroup, not a group of selected nodes.

it’s quite strange to see the operator showing in the 3dview :thinking:… the poll function should block it from happening…

return space.type == 'NODE_EDITOR' and currentNode.type=='GROUP'

Hi @Secrop, sorry for the late reply. It’s a bit crazy lately! I’ll give it a go and let you know about the call operator.

Thank you for your help! :slight_smile:

Thanks man for the nodes… was trying to bake normals… add-on works like a charm

Glad it helped! :slight_smile:

As for ‘works like a charm’, it still doesn’t! :frowning:
At least for any Blender version with the new depsgraph enable…

The good news is that I’ve a patch almost ready for 2.8 (that could be back-transfered to 2.79+). But as this patch makes also possible to have custom compositor/eevee nodes, it still requires some debugging.

1 Like

WIsh you best of luck. Is the install same in 2.8 as in 2.79?

For now, yes; but i don’t advise using it in 2.8 for now (or in 2.79+). Depsgraph is still not ready to deal with NodeCustomGroups, neither is Eevee or Compositor. If the patch gets accepted, I’ll still need to rewrite most of this addon, since it will need to work in the other node systems.

Hi,
Thanks for the info. For some reason my normal bake is turning out black. I have connected the output directly to the Surface connector. I can see the normal in the rendered view on the object but when I bake color it comes out all black.

Bake type should be set to ‘Emit’. :wink: