Forum: Blender


Subject: Blender Python Shader (Cycles) Automation

RobynsVeil opened this issue on Oct 18, 2015 · 32 posts


RobynsVeil posted Sun, 18 October 2015 at 5:25 AM

One of the things I found most compelling about Poser was its extensibility, through Python. Indeed, it made making materials incredible fun, particularly when using Bagginsbill's Matmatic. Whilst there were several who understood the maths Bagginsbill was teaching along with his shaders and who created some pretty incredible shaders (like KobaltKween's Essential Materials, as a vivid, powerful example), I was never that clever. My Matmatic-generated skin shaders were pretty pedestrian, really. Seems almost a waste of a fine add-on tool, really, but what I used it for was more as an automation tool. Now I'm doing a lot in Blender these days, particularly importing scenes posed in Poser (well, the figures, anyway) and ... well, the time-sink is definitely assigning the materials. That's a huge time-involvement issue, particularly when you have to make a small change to a pose, then re-export/import the figure from Poser to Blender. There's no easy, quick way to assign shaders onto a multi-material figure in Blender.

So, I thought: hey, why not use Blender Python to automate this?

Well, this is not a trivial task, apparently. I've been struggling getting started just creating a new material with a glossy, diffuse and mix shader plugged into an output node on a cube. But, finally got that working. 😅

There's a long way to go. I'll work first with a figure I'm most familiar with, automating a fairly rudimentary shader for her. The paths and everything will be hard-coded at first as this is just a very basic script, for now. If anyone is working along the same lines, I'd be happy to share where I am as well as the code and so forth. Hope to get an add-on out of this, even... who knows. 🙂

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


LuxXeon posted Sun, 18 October 2015 at 9:52 PM

Sounds absolutely brilliant, RobynsVeil. I can't wait to see how this turns out. While Blender has a very robust, lively community of coders and developers creating very useful addons, there is an enormous chasm in logical and streamlined Cycles tools. The Node Wrangler is one of only a few out there. Good luck, and congratulations!

______________________________________

My Store
My Free Models
My Video Tutorials
My CG Animations
Instagram: @luxxeon3d
Facebook: https://www.facebook.com/luxxeon


RobynsVeil posted Mon, 19 October 2015 at 4:06 AM

Thanks, LuxXeon. 🙂

I'm a huge proponent of open-source. Something about the philosophy of sharing for the betterment of all really appeals to me. I wish I could say that is what "inspires" me to share this, but that's not the reason at all. In nursing, we call it "share the wealth"... which means: get other professionals to consider your problem: they may actually have a decent solution, but if nothing else, they might help you see your "problem" differently.

So, here is what I was doing and what I was hoping to automate: I create scenes in Poser - most importantly posing the humanoid figure, clothing and hair - which I then export to Blender for rendering. It's not just for Cycles, mind you: in Blender I have access to stuff Poser's going to be a long time catching up with... like distributing grass, creating all manner of trees, like this one: SeaStroll027sm.jpg

...not to mention hair and smoke and fog and-and-and... the imagination is your only limit.

But Poser's material management setup, while not perfect, is significantly superior to Blender's. It is distributing shaders on mat zones of figures and hair that I'm hoping to achieve with this script.

Which means learning a lot... not just Python, but aspects of coding particular to Blender. In the API guide, they mention things I need to avoid doing, things I sort-of got used to doing, coding in Matmatic, which is also Python. Blender's Python has limitations. Actions happen in contexts (so if you're not in the right context for an operator, your action fails). Yes, you can trap for that with polls, but they can also fail.

So, I'll be reading a lot of code as well as studying the API, just to see how others do it. Oh, and apparently the API is a bit of a moving target too, so that will add to the overall fun.

ANYway! I hope it's okay that I publish my code here... this way folks can have a bit of a laugh and we can all grow together.

Here is what I've been able to cobble together so far:

import bpy

if bpy.context.scene.render.engine == 'BLENDER_RENDER':
    bpy.context.scene.render.engine = 'CYCLES'


bpy.ops.mesh.primitive_cube_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0))


selectedObj = bpy.context.active_object


if bpy.data.materials.get("Material") is not None:
    newMat = bpy.data.materials.get("Material")
else:
    newMat = bpy.data.materials.new(name="roughWall")


if len(selectedObj.data.materials):
    selectedObj.data.materials = newMat
else:
    selectedObj.data.materials.append(newMat)

nextMat = bpy.data.materials.new(name = "smoothWall")
selectedObj.data.materials.append(nextMat)
selMats = selectedObj.data.materials
for sMs in selMats:
    sMs.use_nodes = True
    treeNodes = sMs.node_tree
    nodeLinks = treeNodes.links

    for n in treeNodes.nodes:
        treeNodes.nodes.remove(n)

    shDif = treeNodes.nodes.get('ShaderNodeBsdfDiffuse')
    if shDif is None:
        shDif = treeNodes.nodes.new('ShaderNodeBsdfDiffuse')
    shGls = treeNodes.nodes.new('ShaderNodeBsdfGlossy')
    shMix = treeNodes.nodes.new('ShaderNodeMixShader')
    shOut = treeNodes.nodes.get('ShaderNodeOutputMaterial')
    if shOut is None:
        shOut = treeNodes.nodes.new('ShaderNodeOutputMaterial')
    shDif.location = 0, 400
    shGls.location = 0, 200
    shMix.location = 200, 300
    shOut.location = 400, 300
    nodeLinks.new(shDif.outputs[0], shMix.inputs[1])
    nodeLinks.new(shGls.outputs[0], shMix.inputs[2])
    nodeLinks.new(shMix.outputs[0], shOut.inputs[0])

This will put a cube into your scene and create a simple shader consisting of a diffuse, a glossy and a mix node (and of course, an Output node).

Hope to build significantly from here... this is more proof-of-concept code than anything else. I have API to learn, actually, how to find out how things work... that's the most elusive thing, and time-consuming. I think I've exhausted YouTube - always a good source - as the common pattern they show is this: we'll start off learning how to do simple maths in the console, then proceed from there to doing quantum physics by calling modules written in C. 😖

No middle of the road, really. 😕

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


heddheld posted Wed, 21 October 2015 at 3:29 AM

look forward to seeing how this works out hun ..........I'd like a matmatic for cycles too while doing simple shaders is quite easy once I get past about a dozen nodes my brain starts to melt rofl

you can copy the mat data from one object to another ....never tried with a poser doll...but I use it quite a lot


RobynsVeil posted Wed, 21 October 2015 at 4:55 AM

I've been copying shaders from a central figure (using Append->Figure->Node_Tree->All_Nodes for some time, Hedd, but it's tedious. Tedious-tedious-tedious, in the words of Monty Python, for whom the language 'Python' was named after... did you know that?

This is the purpose of the script. Well, ostensibly, anyway: it's also an opportunity to get back into Python. Look, I've never been really good at Matmatic: I was able to re-create Bagginsbill's 'James Shader' (of DNA fame) pretty much, but doing something really original? not in MY DNA. 😏

So, the shader I'm building for Blender is pretty straight-forward. There is no sub-surface-scattering, even. Not yet. To be honest, it's not detectable in scenes like the one, above. Well, not to my knowledge, anyway... 😏

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


heddheld posted Wed, 21 October 2015 at 9:09 AM

didn't think I could show YOU anything hun ;-).......tedious...........guess I'm more OCD then you rofl. I've looked at all the "ubershaders" ( no names but ) most are way to complicated for anyone but the creator to understand ;-( I'd say 90% of what I do can be done in less then a dozen nodes ...........BUT I don't do ultra realistic that's makes it a lot easier

luck hun and any help (I cant do python.......keep promising I will learn it but I got fed up of C lol )


RobynsVeil posted Fri, 23 October 2015 at 5:20 AM

Made a bit more headway. At this point, this is a script not an add-on, so it only runs from the text editor in Blender. Just import a Victoria 4 figure into an empty scene, copy-paste the code into the text editor, change the path on line 107 to reflect where your image files are stored, and the name of the file to reflect which file you use for limbs, and click Run Script.

Eventually, I might make this into a sort of add-on. Just like everything in open-source, we create stuff first for ourselves, and then share it around because we have a generous, community-minded spirit. 😆 Actually, I enjoy sharing 🙂 - it's nice to share - but who knows why folks share stuff... reputation? get your name out there? dunno. Anyway, FWIW, here it is, so far:

# simpleSkinShader001.py
# 
# Copyright (c) 23-Oct-2015, Robyn Hahn. Heaps of borrowed code.
#
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****

bl_info = {
    "name": "Simple skin shader for Poser Figures",
    "author": "Robyn Hahn",
    "version": (0, 01, 1),
    "blender": (2, 76, 0),
    "location": "-= Not a Tool For Blender, Yet =-",
    "description": "Generates a simple Cycles shader for a Poser figure",
    "warning": "early development",
    "wiki_url": "http://0.0.0.1",
    "category": "Simple Material Script"}

import bpy
import os.path

# sets renderer to Cycles
if bpy.context.scene.render.engine == 'BLENDER_RENDER':
    bpy.context.scene.render.engine = 'CYCLES'


# make sure your figure is selected and nothing else
bpy.ops.object.select_all(action='DESELECT')
selObj = bpy.data.objects['Katie-reposed']
selObj.select = True


# function generates node set (shader)
def buildShader(cImgClr, cImgBmp=None):
    selMats = selObj.active_material
    
    selMats.use_nodes = True
    treeNodes = selMats.node_tree
    nodeLinks = treeNodes.links
    
    # clears existing nodes, if any
    for n in treeNodes.nodes:
        treeNodes.nodes.remove(n)
        
    # create nodes: Texture Coordinate and Mapping
    shTcd = treeNodes.nodes.new('ShaderNodeTexCoord')
    shMap = treeNodes.nodes.new('ShaderNodeMapping')
    
    # create nodes: Image Texture
    sImgC = treeNodes.nodes.get('ShaderNodeTexImage')
    if sImgC is None:
        sImgC = treeNodes.nodes.new('ShaderNodeTexImage')    
    sImgC.image = cImgClr
    
    # create nodes: Diffuse
    shDif = treeNodes.nodes.get('ShaderNodeBsdfDiffuse')
    if shDif is None:
        shDif = treeNodes.nodes.new('ShaderNodeBsdfDiffuse')
    
    # create nodes: Glossy and Mix
    shGls = treeNodes.nodes.new('ShaderNodeBsdfGlossy')
    shMix = treeNodes.nodes.new('ShaderNodeMixShader')
    
    # create node: Material Output (sort-of like PoserSurface)
    shOut = treeNodes.nodes.get('ShaderNodeOutputMaterial')
    if shOut is None:
        shOut = treeNodes.nodes.new('ShaderNodeOutputMaterial')

    # Set node locations, roughly
    shTcd.location = -850, 300
    shMap.location = -600, 300
    sImgC.location = -200, 300
    shDif.location = 0, 400
    shGls.location = 0, 200
    shMix.location = 200, 300
    shOut.location = 400, 300

    # Link nodes
    nodeLinks.new(shTcd.outputs[2], shMap.inputs[0])
    nodeLinks.new(shMap.outputs[0], sImgC.inputs[0])
    nodeLinks.new(sImgC.outputs[0], shDif.inputs[0])
    nodeLinks.new(shDif.outputs[0], shMix.inputs[1])
    nodeLinks.new(shGls.outputs[0], shMix.inputs[2])
    nodeLinks.new(shMix.outputs[0], shOut.inputs[0])
    return selMats

bpy.context.object.active_material_index = 0

"""So far, this will set the skin shader for any limbs materials"""
for i in range(27):
    selObj.active_material_index = i
    path = '/home/robyn/Documents/Blender/Projects/AllTextures/AllSkin/textures/'
    imgFile = 'Syri_Limbs.jpg'
    fullImg = path + imgFile
    img = bpy.data.images.load(filepath = fullImg)
    matName = selObj.active_material.name
    matType = matName[0:1]
    if matType == '3':
        newMat = buildShader(img)

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Fri, 23 October 2015 at 5:39 AM

Oh, oops. The line that says:

"version": (0, 01, 1),

in the bl_info section needs to be changed to:

"version": (0, 1, 1),

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Fri, 23 October 2015 at 5:44 AM

Okay, a bit more done: this will now put the absolute simplest of shaders on all skin surfaces. I'll flesh out the shader a bit next, then add other surfaces thereafter.

# simpleSkinShader001.py
# 
# Copyright (c) 23-Oct-2015, Robyn Hahn. Heaps of borrowed code.
#
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****

bl_info = {
    "name": "Simple skin shader for Poser Figures",
    "author": "Robyn Hahn",
    "version": (0, 1, 2),
    "blender": (2, 76, 0),
    "location": "-= Not a Tool For Blender, Yet =-",
    "description": "Generates a simple Cycles shader for a Poser figure",
    "warning": "early development",
    "wiki_url": "http://0.0.0.1",
    "category": "Simple Material Script"}

import bpy
import os.path

# sets renderer to Cycles
if bpy.context.scene.render.engine == 'BLENDER_RENDER':
    bpy.context.scene.render.engine = 'CYCLES'


# make sure your figure is selected and nothing else
bpy.ops.object.select_all(action='DESELECT')
selObj = bpy.data.objects['Katie-reposed']
selObj.select = True


# function generates node set (shader)
def buildShader(cImgClr, cImgBmp=None):
    selMats = selObj.active_material
    
    selMats.use_nodes = True
    treeNodes = selMats.node_tree
    nodeLinks = treeNodes.links
    
    # clears existing nodes, if any
    for n in treeNodes.nodes:
        treeNodes.nodes.remove(n)
        
    # create nodes: Texture Coordinate and Mapping
    shTcd = treeNodes.nodes.new('ShaderNodeTexCoord')
    shMap = treeNodes.nodes.new('ShaderNodeMapping')
    
    # create nodes: Image Texture
    sImgC = treeNodes.nodes.get('ShaderNodeTexImage')
    if sImgC is None:
        sImgC = treeNodes.nodes.new('ShaderNodeTexImage')    
    sImgC.image = cImgClr
    
    # create nodes: Diffuse
    shDif = treeNodes.nodes.get('ShaderNodeBsdfDiffuse')
    if shDif is None:
        shDif = treeNodes.nodes.new('ShaderNodeBsdfDiffuse')
    
    # create nodes: Glossy and Mix
    shGls = treeNodes.nodes.new('ShaderNodeBsdfGlossy')
    shMix = treeNodes.nodes.new('ShaderNodeMixShader')
    
    # create node: Material Output (sort-of like PoserSurface)
    shOut = treeNodes.nodes.get('ShaderNodeOutputMaterial')
    if shOut is None:
        shOut = treeNodes.nodes.new('ShaderNodeOutputMaterial')

    # Set node locations, roughly
    shTcd.location = -850, 300
    shMap.location = -600, 300
    sImgC.location = -200, 300
    shDif.location = 0, 400
    shGls.location = 0, 200
    shMix.location = 200, 300
    shOut.location = 400, 300

    # Link nodes
    nodeLinks.new(shTcd.outputs[2], shMap.inputs[0])
    nodeLinks.new(shMap.outputs[0], sImgC.inputs[0])
    nodeLinks.new(sImgC.outputs[0], shDif.inputs[0])
    nodeLinks.new(shDif.outputs[0], shMix.inputs[1])
    nodeLinks.new(shGls.outputs[0], shMix.inputs[2])
    nodeLinks.new(shMix.outputs[0], shOut.inputs[0])
    return selMats

bpy.context.object.active_material_index = 0

"""So far, this will set the skin shader for any limbs materials"""
for i in range(27):
    bPasteTex = False 
    selObj.active_material_index = i
    path = '/home/robyn/Documents/Blender/Projects/AllTextures/AllSkin/textures/'
    matName = selObj.active_material.name
    matType = matName[0:1]
    if matType == '3':
        imgFile = 'Syri_Limbs.jpg'
        bPasteTex = True
    if matType == '2':
        imgFile = 'Syri_Torso.jpg'
        bPasteTex = True
    if matType == '1':
        imgFile = 'Syri_Face.jpg'
        bPasteTex = True
    fullImg = path + imgFile
    img = bpy.data.images.load(filepath = fullImg)

    if bPasteTex:
        newMat = buildShader(img)

Again, you want to change the path, and the names of the files to reflect those that you have.

Off to play with making fun shaders! 😉

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Sun, 25 October 2015 at 6:29 AM

Not sure if anyone wants to play with this, but here goes. This is very much a proof-of-concept thing this far in, nothing more. I won't talk to suitability or anything else: just having a wee bit of fun. Here is the code:

# FullSkin024.py
# 
# Copyright (c) 25-Oct-2015, Robyn Hahn
#
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****

bl_info = {
    "name": "Simple skin shader for Poser Figures",
    "author": "Robyn Hahn",
    "version": (0, 2, 4),
    "blender": (2, 76, 0),
    "location": "-= Not a real addon For Blender, yet =-",
    "description": "Generates a simple Cycles shader for a Poser figure",
    "warning": "early development",
    "wiki_url": "http://0.0.0.1",
    "category": "Simple Material Script"}

import bpy
import os.path

"""
These are the items you change:
path     ... the fully-qualified path to your texture files like C:MyDirMyTex
curr_obj ... the name of your imported V4 figure   
clr files... colour maps 
bmp files... bump maps
"""
path = '/home/robyn/Documents/Blender/Projects/AllTextures/AllSkin/textures/'
curr_obj = bpy.data.objects['V4']
clrLimbs = 'Syri_Limbs.jpg'
bmpLimbs = 'Syri_LimbsB.jpg'
clrTorso = 'Syri_Torso.jpg'
bmpTorso = 'Syri_TorsoB.jpg'
clr_Face = 'Syri_Face.jpg'
bmp_Face = 'Syri_FaceB.jpg'



# sets renderer to Cycles
if bpy.context.scene.render.engine == 'BLENDER_RENDER':
    bpy.context.scene.render.engine = 'CYCLES'


# class builds mat-specific node set (shader)
class buildShader():
    def __init__(self, cObj, cRegion, ImgClr, ImgBum=None):
        self.selObj = cObj
        self.Region = cRegion
        self.ImgCol = ImgClr
        self.ImgBmp = ImgBum

        self.selObj.select = True
        
        self.selMats = self.selObj.active_material
        self.selMats.use_nodes = True
        
        self.treeNodes = self.selMats.node_tree
        self.nodeLinks = self.treeNodes.links
        
        self.makeSkin()
    
    def makeSkin(self):
        # clears existing nodes, if any
        for n in self.treeNodes.nodes:
            self.treeNodes.nodes.remove(n)
            
        # create nodes: Texture Coordinate and Mapping
        shaTcd = self.treeNodes.nodes.new('ShaderNodeTexCoord')
        shaMap = self.treeNodes.nodes.new('ShaderNodeMapping')
        
        # create node: Colour Image Texture
        ImgCol = self.treeNodes.nodes.new('ShaderNodeTexImage')    
        ImgCol.image = self.ImgCol
        ImgCol.color_space = 'COLOR'
        
        # create node: Bump Image Texture
        ImgBmp = self.treeNodes.nodes.new('ShaderNodeTexImage')    
        ImgBmp.image = self.ImgBmp
        ImgBmp.color_space = 'NONE'


        # create nodes: Diffuse, set second input [1] roughness to .15
        shaDif = self.treeNodes.nodes.new('ShaderNodeBsdfDiffuse')
        shaDif.inputs[1].default_value = .15
        
        # create node: Translucent
        shaTrn = self.treeNodes.nodes.new('ShaderNodeBsdfTranslucent')
        shaTrn.inputs[0].default_value = [.8, .22, .06, 1]
        
        # create node: Glossy
        shaGls = self.treeNodes.nodes.new('ShaderNodeBsdfGlossy')
        shaGls.inputs[1].default_value = .12

        # create node: Mix-RGB
        clrMix = self.treeNodes.nodes.new('ShaderNodeMixRGB')
        clrMix.inputs[0].default_value = .1
        clrMix.inputs[2].default_value = [.5, .35, .275, 1]

        mathMl = self.treeNodes.nodes.new('ShaderNodeMath')
        mathMl.operation = 'MULTIPLY'
        mathMl.inputs[1].default_value = .01

        shaMx3 = self.treeNodes.nodes.new('ShaderNodeMixShader')
        shaMx2 = self.treeNodes.nodes.new('ShaderNodeMixShader')
        shaMx1 = self.treeNodes.nodes.new('ShaderNodeMixShader')
        
        # create node: Material Output (sort-of like PoserSurface)
        shaOut = self.treeNodes.nodes.new('ShaderNodeOutputMaterial')

        # Set node locations, roughly
        shaTcd.location = -850, 300
        shaMap.location = -600, 300
        ImgCol.location = -200, 400
        ImgBmp.location = -200, 200
        shaDif.location = 0, 400
        shaTrn.location = 0, 200
        clrMix.location = 0, 100
        shaMx3.location = 200, 400
        shaMx2.location = 400, 400
        shaGls.location = 400, 100
        shaMx1.location = 600, 400
        mathMl.location = 600, 100
        shaOut.location = 800, 300

        # Link nodes: tex cood and mapping
        self.nodeLinks.new(shaTcd.outputs[2], shaMap.inputs[0])
        self.nodeLinks.new(shaMap.outputs[0], ImgCol.inputs[0])
        self.nodeLinks.new(shaMap.outputs[0], ImgBmp.inputs[0])
        # Link nodes: clr and bmp
        self.nodeLinks.new(ImgCol.outputs[0], shaDif.inputs[0])
        self.nodeLinks.new(ImgCol.outputs[0], clrMix.inputs[1])
        self.nodeLinks.new(ImgBmp.outputs[0], mathMl.inputs[0])
        # Mixing colorMap and Translucent ( reddish )
        self.nodeLinks.new(shaDif.outputs[0], shaMx3.inputs[1])
        self.nodeLinks.new(shaTrn.outputs[0], shaMx3.inputs[2])
        shaMx3.inputs[0].default_value = .065
        # Mixing ClrMap/Translu and ClrMix ( beige )
        self.nodeLinks.new(shaMx3.outputs[0], shaMx2.inputs[1])
        self.nodeLinks.new(clrMix.outputs[0], shaMx2.inputs[2])
        shaMx2.inputs[0].default_value = .05
        # Mixing ClrMap/Transl/ClrMix and Glossy
        self.nodeLinks.new(shaMx2.outputs[0], shaMx1.inputs[1])
        self.nodeLinks.new(shaGls.outputs[0], shaMx1.inputs[2])
        shaMx1.inputs[0].default_value = .05
        
        # Output
        self.nodeLinks.new(shaMx1.outputs[0], shaOut.inputs[0])
        self.nodeLinks.new(mathMl.outputs[0], shaOut.inputs[2])
        
        return self.selMats
        

# make sure your figure is selected and nothing else
# bpy.ops.object.select_all(action='DESELECT')
bpy.context.object.active_material_index = 0

path = '/home/robyn/Documents/Blender/Projects/AllTextures/AllSkin/textures/'
curr_obj = bpy.data.objects['V4']

"""So far, this will set the skin shader for any limbs materials"""
for i in range(27):
    bPasteTex = False 
    curr_obj.active_material_index = i
    matName = curr_obj.active_material.name
    matType = matName[0:1]
    if matType == '3':
        img_Clr = clrLimbs
        img_Bmp = bmpLimbs
        bPasteTex = True
    if matType == '2':
        img_Clr = clrTorso
        img_Bmp = bmpTorso
        bPasteTex = True
    if matType == '1':
        img_Clr = clr_Face
        img_Bmp = bmp_Face
        bPasteTex = True

    if bPasteTex:
        ImgColour = path + img_Clr
        ImgBump = path + img_Bmp
        imgC = bpy.data.images.load(filepath = ImgColour)
        imgB = bpy.data.images.load(filepath = ImgBump)

        newMat = buildShader(curr_obj, 'Skin', imgC, imgB)

At this point, this simple script paints V4 with your favourite skin textures and a shader of sorts. If you want to manually play with the code, you can tweak the values.

Here are the steps to take to make this work:

-- Save the content of the above script to a plain-text editor file, call it whatever you want.
         (I call mine 'FullSkin024.py', because it's Python code, version .24.)
-- Open Blender (getting rid of the default cube and that)
-- File -> Import -> Wavefront (obj) ... navigate to your Vicky, select her OBJ (not her MTL), then chose the following Import settings:
---- Untick Smooth Groups and untick Lines and everything in Split By
---- Tick Keep Vert Order and tick Poly Groups
-- Import OBJ
-- Click on her in the scene, then hit [S] (for scale), 10
-- Select Scripting from the Scene Selector (next to 'Help') - usually says 'Default' by default 😉
-- In the Text Editor section, select Open and navigate to the code file (the one I call 'FullSkin024.py')
-- Scroll down to line 44 and make changes to:
---- path
---- name of your Vickie - whatever she's called in Outliner
---- the names of your V4 texture (colour) and bump files for the face, torso and limbs
-- Run the script

I'll sit over here in Oz with my fingers crossed hoping it will work for you. 😅

If this actually works, you can save the script with the changes you put in.

If anyone actually tries this, please let me know if it worked or if you ran into dramas or whatever. 🙂

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


EClark1894 posted Sun, 25 October 2015 at 3:59 PM

Will it work on any figure, or just V4?




RobynsVeil posted Sun, 25 October 2015 at 5:11 PM

At this point, it's only V4 and it's only skin, Eric. I'm looking at using collections - like I did in Matmatic: actually, "borrowing" a LOT of concepts I learned in from Bagginsbill - and then, the script will work on any figure I have a definition for. In matmatic, I had collections for Daz figures as well as Dawn and some of the lesser known ones.

I'll actually focus on getting the rest of the material zones working, then seeing about doing the collections thing. Stay tuned... 😀

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 26 October 2015 at 6:41 PM

The first stage is finally finished. All materials except eyebrows have been addressed. There's definitely nothing exotic about the materials - pretty rudimentary - but this provides a bit of a framework to start from.

At this point, running the script involves the same steps outlined above. It's gotten a bit long to post the whole thing on here, so I've zipped it up for download here.

Enjoy 😅 (and please let me know if you run into dramas - I've tested on another system, but I'm only running Linux atm so no idea how this will work in another environment).

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 26 October 2015 at 7:18 PM

A few more caveats: if you've renamed your V4 materials, this won't work. Also, sometimes during export-from-Poser -> import-to-Blender, an empty, un-named material is generated. I'll need to code for this, but for the moment, just delete that material before running the script.

Another thing I noticed is that I've manually counted the material slots to iterate through (KLUDGE!!!) - I'll need to change this so that the script picks up how many slots there are. I counted 27, but with the eyebrows included there are 28 .... {{{sigh}}} ...another thing to fix. 😟

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 26 October 2015 at 7:56 PM

Hm, that wasn't trivial. Thank goodness for BlenderArtists..org. Fixed.

The script will now iterate through all materials and will ignore any material slots with no names.

If you downloaded this, just re-download it and over-write the old script.

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 26 October 2015 at 8:11 PM

Updated instructions for after you've downloaded the script:

-- Unzip the script to somewhere you can find it easily in Blender.
-- Open Blender (getting rid of the default cube and that)
-- File -> Import -> Wavefront (obj) ... navigate to your Vicky, select her OBJ (not her MTL), then chose the following Import settings:
---- Untick Smooth Groups and untick Lines and everything in 'Split By'
---- Tick 'Keep Vert Order' and tick 'Poly Groups'
-- Import OBJ
-- Click on the figure in the scene, press [S] (for scale), and type 10
-- Select Scripting from the Scene Selector (next to 'Help' up the top) - usually says 'Default' by default  😉 
-- In the Text Editor section, select Open and navigate to the code file (called 'FullV4028.py')
-- Scroll down to line 258 (might need to enable line numbering, furthest icon of the three to the left of 'Run Script') and change:
---- path:                             path to your texture files: the images to paint on V4
---- figurObj:                        name of your Vickie - whatever she's called in Outliner
---- clrLimbs / bmpLimbs etc: the names of your V4 texture (colour) and bump files for the face, torso and limbs
-- Run the script

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 26 October 2015 at 9:54 PM

RobynsVeil posted at 12:53PM Tue, 27 October 2015 - #4235228

At this point, it's only V4 and it's only skin, Eric. I'm looking at using collections - like I did in Matmatic: actually, "borrowing" a LOT of concepts I learned in from Bagginsbill - and then, the script will work on any figure I have a definition for. In matmatic, I had collections for Daz figures as well as Dawn and some of the lesser known ones.

I'll actually focus on getting the rest of the material zones working, then seeing about doing the collections thing. Stay tuned... 😀

Sheesh, I called you 'Eric'. Forgive these grey hairs, Earl.... sorry about that! 😟

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Tue, 27 October 2015 at 1:00 AM

Realised something else - the shader expects a .png for the lashes. Here it is.

My bad. 😊

I'm currently working on the Dawn version - should be out in a wee bit.

ETA: correction... might be a bit longer. Dawn comes with spec maps, not bump maps. Which means: bump will be a procedure or some tileable normal map, and gloss has to be driven by the maps. So, a bit of change to the shader code.

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


heddheld posted Tue, 27 October 2015 at 8:15 PM

jees girl take a cold shower .............you not been this fired up for a while ;-)

guess this means your a snakehead now rofl.............never lose the bigger picture .........with your medical stuff and confusser stuff lol

a nice animation of a heart .......yes fluids and physics and what happens when a Taser whacks it ..........even junior doctors would buy the dvd that's the retirement sorted

will look at this tomorrow but looks like your off to a flying start


RobynsVeil posted Sun, 01 November 2015 at 4:57 AM

Lol... you've got a point there Hedd 🙂 ,perhaps I am obsessing a bit too much on this. I've even got a github site for the script now. Github is its own complexity, oh my! Anyway, on my tablet so a bit hard to provide links but it's under robinboncoeur/Shader-Addon.

Don't forget to download the pngs.

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


chaecuna posted Sun, 01 November 2015 at 7:24 AM

Main github url https://github.com/robinboncoeur, specific repository https://github.com/robinboncoeur/Shaders-Addon (notice the s in shaders).


RobynsVeil posted Sun, 01 November 2015 at 1:50 PM

Thanks chaecuna, those trailing 's'es are my undoing when I'm writing code, too. 😏

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Sun, 01 November 2015 at 2:15 PM

Not sure where to post this question, really, but as it does involve the end user's experience and since you and Poser users are my target users for this script, I guess I'll ask here.

At this point, there are two sections in the script that need to be edited, line 47:

sys.path.append("/home/robyn/Documents/Blender/Projects/AllScripts")

... a hard-coded path statement that lets the script (this one, currently 'Shaders032.py) know where to find the figureDefs.py dictionary file: information, I'm sure, I can obtain with code...

... and this mess, starting on line 291:

    figurObj = 'V4'   # also used to identify fig in figureDefs.py
    textPath = '/home/robyn/Documents/Blender/Projects/AllTextures/AllSkin/V4/'

    clrALimb = 'Syri_Limbs.jpg'
    clrLLimb = 'Syri_Limbs.jpg'
    bmpALimb = 'Syri_LimbsB.jpg'
    bmpLLimb = 'Syri_LimbsB.jpg'
    spcALimb = None
    spclLimb = None
    clr_Body = 'Syri_Torso.jpg'
    bmp_Body = 'Syri_TorsoB.jpg'
    spc_Body = None
    clr_Face = 'Syri_Face.jpg'
    bmp_Face = 'Syri_FaceB.jpg'
    spc_Face = None
    clr_Eyes = 'EyesMochachino.jpg'
    clrMouth = 'Syri_Teeth.jpg'
    clr_Lash = 'BaseLashes05.png'

Which the first is no biggie, finding a solution for the second has been a bit of a quandary.

Clearly, it has to come out of the script. That's key. I've toyed with config files that live with the other two scripts, but then those will need to be somehow swapped out when a new figure with a different set of colour, bump and spec maps (images) gets loaded. I guess what would be easiest - for me - is to set up a 'dictionary' file that gets written to - and lives with - the texture/colour image files. Basically, the 'dictionary would look like this:

dictMaps = {
    'clrALimb': 'Syri_Limbs.jpg',
    'clrLLimb': 'Syri_Limbs.jpg',
    'bmpALimb': 'Syri_LimbsB.jpg',
    'bmpLLimb': 'Syri_LimbsB.jpg',
    'spcALimb': None,
    'spclLimb': None,
    'clr_Body': 'Syri_Torso.jpg',
    'bmp_Body': 'Syri_TorsoB.jpg',
    'spc_Body': None,
    'clr_Face': 'Syri_Face.jpg',
    'bmp_Face': 'Syri_FaceB.jpg',
    'spc_Face': None,
    'clr_Eyes':  'EyesMochachino.jpg',
    'clrMouth': 'Syri_Teeth.jpg',
    'clr_Lash': 'BaseLashes05.png',
}

The figurObj value could be assigned by a dropdown in a panel app (needs to be one referenced in the figureDef.py dictionary, like 'V4' or 'Dawn') and textPath I would get from the user navigating to where the images and this 'dictionary' in a imageList.py.

Does all that seem too hard? 😅

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Sun, 01 November 2015 at 2:32 PM

So, like I've put this into the folder that contains the texture files for my V4 figure:

class imageDict():
    def __init__(self):
        self.dictMaps = {}
        self.listMaps()
        
    def listMaps(self):
        dictMaps = {
            'clrALimb': 'Syri_Limbs.jpg',
            'clrLLimb': 'Syri_Limbs.jpg',
            'bmpALimb': 'Syri_LimbsB.jpg',
            'bmpLLimb': 'Syri_LimbsB.jpg',
            'spcALimb': None,
            'spclLimb': None,
            'clr_Body': 'Syri_Torso.jpg',
            'bmp_Body': 'Syri_TorsoB.jpg',
            'spc_Body': None,
            'clr_Face': 'Syri_Face.jpg',
            'bmp_Face': 'Syri_FaceB.jpg',
            'spc_Face': None,
            'clr_Eyes':  'EyesMochachino.jpg',
            'clrMouth': 'Syri_Teeth.jpg',
            'clr_Lash': 'BaseLashes05.png',
        }

...and saved it as imageList.py. The bit that needs to be edited by the user starts on line 8. Does this all look a bit too complicated for end users? 😨

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 09 November 2015 at 4:05 PM

Update: I've decided for the sake of simplicity to make that imageList.py into a .csv file. Easy to read and write to by the end user, and very quick to read in my modules (and straightforward). The image_List.csv file is saved along with the image files themselves, which then gives me the path info to the images for the module.

I've gone to an __ init __ .py for the panel, with the actual shader builder class in a separate file, as well as the figure_Defs.py dictionary (so far, has V4, Mariko, Antonia and Dawn defined) ... that's it. Just need to nut out property vs variable scope. The latter I'm okay with; the other - the property thingie - is a bit more troublesome.

Been doing a fair bit of posting these days on Blender.StackExchange - the folk on there been very helpful. Just FYI, in case you're contemplating writing a module yourself: the API has changed, so some of the examples you see in older tutorials simply don't work at all: they will throw an error. What has changed? bpy.context and bpy.data prefixes are no longer allowed in any modules (including those you 'import') except in your main panel draw() and execute() functions. This is to avoid issues with the module waking up in the wrong context where those references would be inappropriate, I suppose. Scripts loaded and run from the text edit are fine: those don't have that restriction: only modules you install do.

Will hopefully have a module ready to muck around with in the not-too-distant future. 😏

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Mon, 09 November 2015 at 4:16 PM

BTW, anyone masochistic enough to wish to follow the progress of this, feel free to have a look here. And absolutely feel free to 'pull' if you wish and have some ideas you might think could work. This is FOSS, so everyone owns it. 😄

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


chaecuna posted Tue, 10 November 2015 at 12:39 AM

The only comment is to move every data related .py into a simple data file and define a data directory to automatically scanned at startup containing the definitions. This way, to extend the plugin you have only to add other definitions to the data directory and restart the plugin.


RobynsVeil posted Tue, 10 November 2015 at 3:27 PM

Thanks, chaecuna. I'm actually struggling with exactly that (the data file path, to be exact). The script needs to be os-agnostic, so it's been suggested on stack.exchange to use relative paths, and also, to select just the data file itself (I've decided on .csv for the sake of simplicity: can be opened with a text editor or spreadsheet by users and is easy to sort out what's doing what) which could contain a fully-qualified path statement, I suppose. The csv looks as follows, currently:

FieldName, ImageName
"clrALimb", "Syri_Limbs.jpg"
"clrLLimb", "Syri_Limbs.jpg"
"bmpALimb", "Syri_LimbsB.jpg"
"bmpLLimb", "Syri_LimbsB.jpg"
"spcALimb", "Syri_LimbsS.jpg"
"spclLimb", "Syri_LimbsS.jpg"
"clr_Body", "Syri_Torso.jpg"
"bmp_Body", "Syri_TorsoB.jpg"
"spc_Body", "Syri_TorsoS.jpg"
"clr_Face", "Syri_Face.jpg"
"bmp_Face", "Syri_FaceB.jpg"
"spc_Face", "Syri_FaceS.jpg"
"clr_Eyes", "EyesHazel.jpg"
"clrMouth", "Syri_Teeth.jpg"
"bmpMouth", "Syri_TeethB.jpg"
"clr_Lash", "BaseLashes05.png"

...and is stored with the image files. All this can be re-thought, though. 😏

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


chaecuna posted Wed, 11 November 2015 at 3:58 AM

What we need is something like Poser PZ2/MT5/MC6 or Studio presets for Blender. I will do some meditations on the question in the weekend.


RobynsVeil posted Wed, 11 November 2015 at 5:25 AM

What would be truly amazing is if someone were to develop a Matmatic for Cycles. Matmatic essentially uses the science of materials (and BB's knowledge of which nodes do what) to build very intricate, highly sophisticated shaders using math formulae. This is leveraging of Python's strengths and the best Firefly has to offer. Now, with Cycles coming to Poser - forgot what they're calling it - the materials produced with such a tool would bound to be infinitely more refined and accurate.

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Sat, 14 November 2015 at 9:29 PM

Wee update... the thing runs in a panel now. I've changed the name of the project, so the link above doesn't work: this one does. Testing in Linux only so far... will test in Windows next.

There is still a lot to do, so it's kind-of rough. Error-checking/trapping is minimal. Still a long way to go before this is at all robust, but hey, it's a start! 😄

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks


RobynsVeil posted Sun, 15 November 2015 at 2:19 PM

Shaders-Addon has been sort-of tested in Windows now. Seems to work fine, but then, I've discovered from other stuff I've written that since I wrote it, I would do everything right whereas other people might take a more "creative" approach, with varying results. I guess that is why I need folk testing this. 😐

Monterey/Mint21.x/Win10 - Blender3.x - PP11.3(cm) - Musescore3.6.2

Wir sind gewohnt, daß die Menschen verhöhnen was sie nicht verstehen
[it is clear that humans have contempt for that which they do not understand] 

Metaphor of Chooks