# sibl.py (c) 2013 Michel J. Anders (varkenvarken) # # ***** 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": "Add Environment Nodes", "author": "Michel J. Anders (varkenvarken)", "version": (1, 0, 20150313), "blender": (2, 73, 0), "location": "Node editor > Add > Add Sibl Environment, Add General Environment", "description": "Adds environment lighting based on .ibl file or separate background and .hdr files", "warning": "", "wiki_url": "", "tracker_url": "", "category": "Node"} import configparser import os.path import bpy from bpy.props import * from bpy_extras.io_utils import ImportHelper def add_node(context, nodetype, settings = {}): bpy.ops.node.add_node(type=nodetype.__name__) node = context.active_node for key,val in settings.items(): setattr(node,key,val) return node def get_node(context,nodetype, settings = {}): space = context.space_data nodetree = space.node_tree for n in nodetree.nodes: if isinstance(n, nodetype): return n return add_node(context, nodetype, settings) def link_nodes(nodetree, fromnode, fromsocket, tonode, tosocket): socket_in = tonode.inputs[tosocket] socket_out = fromnode.outputs[fromsocket] return nodetree.links.new(socket_in, socket_out) def main(operator,context,bg=None,ev=None): space = context.space_data node_tree = space.node_tree node_active = context.active_node node_selected = context.selected_nodes if operator.replace_all_nodes : node_tree.nodes.clear() W = 200 H = 100 bg1 = add_node(context, bpy.types.ShaderNodeBackground, {'location': [2*W,0], 'hide':True}) bg2 = add_node(context, bpy.types.ShaderNodeBackground, {'location': [2*W,2*H], 'hide':True}) wo = get_node(context, bpy.types.ShaderNodeOutputWorld, {'location': [3*W,int(1.5*H)], 'hide':True}) mx = add_node(context, bpy.types.ShaderNodeMixShader, {'location': [int(2.5*W),int(1.5*H)], 'hide':True}) ev1 = add_node(context, bpy.types.ShaderNodeTexEnvironment, {'location': [W,H]}) if bg is not None: ev1.image = bg else: ev1.use_custom_color = True ev1.color = [1,0,0] ev2 = add_node(context, bpy.types.ShaderNodeTexEnvironment, {'location': [W,4*H]}) if ev is not None: ev2.image = ev else: ev2.use_custom_color = True ev2.color = [1,0,0] lp = get_node(context, bpy.types.ShaderNodeLightPath, {'location': [W,int(1.5*H)], 'hide':True}) mp = add_node(context, bpy.types.ShaderNodeMapping, {'location': [-W,2*H]}) tx = get_node(context, bpy.types.ShaderNodeTexCoord, {'location': [-2*W,2*H], 'hide':True}) link_nodes(node_tree, mx, "Shader", wo, "Surface") link_nodes(node_tree, bg1, "Background", mx, 2) link_nodes(node_tree, bg2, "Background", mx, 1) link_nodes(node_tree, lp, "Is Camera Ray", mx, "Fac") link_nodes(node_tree, ev1, "Color", bg1, "Color") link_nodes(node_tree, ev2, "Color", bg2, "Color") link_nodes(node_tree, mp, "Vector", ev1, "Vector") link_nodes(node_tree, mp, "Vector", ev2, "Vector") link_nodes(node_tree, tx, "Generated", mp, "Vector") def first(*args): for i in args: if i != "": return i return None class SiblEnvironment(bpy.types.Operator, ImportHelper): # """Create world environment nodes from SIBL archive""" bl_idname = "node.sibl_environment" bl_label = "Sibl Environment" filename_ext = ".ibl" filter_glob = StringProperty( default="*.ibl", options={'HIDDEN'}, ) directory = StringProperty( name="Directory", description="Directory used for importing the file", maxlen=1024, subtype='DIR_PATH', ) replace_all_nodes = BoolProperty(name="Replace All Nodes", description="Delete all existing world nodes before adding sibl nodes", default=True) use_reflection_map = BoolProperty(name="Use Reflection Map", description="Use Reflection map if present (Environment map otherwise)", default=False) def draw(self, context): layout = self.layout layout.prop(self, 'use_reflection_map') layout.prop(self, 'replace_all_nodes') @classmethod def poll(cls, context): space = context.space_data # TODO add additional restriction cycles only return space.type == 'NODE_EDITOR' and space.shader_type == 'WORLD' and space.tree_type == 'ShaderNodeTree' and context.scene.world.use_nodes def execute(self, context): if self.filepath != "": config = configparser.ConfigParser() config.read(self.filepath) # note that the enviroment section is misspelled in the .ibl! # if that's ever corrected this will still work try: evf0 = config['Environment']['EVfile'].strip('"') except KeyError: evf0 = config['Enviroment']['EVfile'].strip('"') bgf0 = config['Background']['BGfile'].strip('"') ref0 = config['Reflection']['REFfile'].strip('"') bgf = first(bgf0, ref0, evf0) evf = first(evf0, ref0, bgf0) ref = first(ref0, bgf0, evf0) if self.use_reflection_map: evf = ref bg = None ev = None try: bg = bpy.data.images.load(os.path.join(self.directory, bgf)) except RuntimeError: pass try: ev = bpy.data.images.load(os.path.join(self.directory, evf)) except RuntimeError: pass main(self,context,bg,ev) else: return {'CANCELLED'} return {'FINISHED'} class GeneralEnvironment(bpy.types.Operator, ImportHelper): # """Create world environment nodes from separate files 3""" bl_idname = "node.general_environment" bl_label = "General Environment" files = CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}) directory = StringProperty( name="Directory", description="Directory used for importing the file", maxlen=1024, subtype='DIR_PATH', ) filter_image = BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'}) filter_folder = BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'}) replace_all_nodes = BoolProperty(name="Replace All Nodes", description="Delete all existing world nodes before adding environment nodes", default=True) use_reflection_map = BoolProperty(name="Use Reflection Map", description="Use Reflection map if present (Environment map otherwise)", default=False) def draw(self, context): layout = self.layout layout.prop(self, 'use_reflection_map') layout.prop(self, 'replace_all_nodes') def draw(self, context): layout = self.layout layout.prop(self, 'replace_all_nodes') @classmethod def poll(cls, context): space = context.space_data # TODO add additional restriction cycles only return space.type == 'NODE_EDITOR' and space.shader_type == 'WORLD' and space.tree_type == 'ShaderNodeTree' and context.scene.world.use_nodes def execute(self, context): print(self.directory) for f in self.files: print(f.name) if len(self.files) > 0 and len(self.files) < 3: bgf=self.files[0].name evf=bgf if len(self.files) > 1: evf = self.files[1].name (name, ext) = os.path.splitext(evf) if ext not in {'.hdr','.exr'}: t = bgf; bgf = evf; evf = t bg = None ev = None try: bg = bpy.data.images.load(os.path.join(self.directory, bgf)) except RuntimeError: pass try: ev = bpy.data.images.load(os.path.join(self.directory, evf)) except RuntimeError: pass main(self,context,bg,ev) else: return {'CANCELLED'} return {'FINISHED'} def menu_func_sibl(self, context): self.layout.operator(SiblEnvironment.bl_idname, text="Add Sibl Environment", icon='PLUGIN') def menu_func_gen(self, context): self.layout.operator(GeneralEnvironment.bl_idname, text="Add General Environment", icon='PLUGIN') def register(): bpy.utils.register_class(SiblEnvironment) bpy.types.NODE_MT_add.append(menu_func_sibl) bpy.utils.register_class(GeneralEnvironment) bpy.types.NODE_MT_add.append(menu_func_gen) def unregister(): bpy.utils.unregister_class(SiblEnvironment) bpy.utils.unregister_class(GeneralEnvironment) if __name__ == "__main__": register()