2020 "name" : "PBR" ,
2121 "description" : "PBR Workflow Tools" ,
2222 "author" : "Digiography.Studio" ,
23- "version" : (1 , 2 , 0 ),
23+ "version" : (1 , 5 , 0 ),
2424 "blender" : (2 , 79 , 0 ),
2525 "location" : "Properties > Material > PBR Material" ,
2626 "wiki_url" : "https://github.com/Digiography/blender_addon_pbr/wiki" ,
@@ -192,7 +192,7 @@ def execute(self, context):
192192 node .color_space = 'NONE'
193193 node .name = 'ds_pbr_texture_normal'
194194 _material_links .new (node .outputs ['Color' ], node_map .inputs ['Color' ])
195-
195+
196196 ds_pbr_auto_textures .execute (self ,context )
197197
198198 return {'FINISHED' }
@@ -297,6 +297,11 @@ class ds_pbr_addon_prefs(AddonPreferences):
297297 description = "Add Metaliic Map Node" ,
298298 default = False
299299 )
300+ option_relative = BoolProperty (
301+ name = "Relative Paths" ,
302+ description = "Use Relative Paths for images." ,
303+ default = True
304+ )
300305 option_metallic = FloatProperty (
301306 name = "Metaliic" ,
302307 description = "A float property" ,
@@ -311,13 +316,14 @@ class ds_pbr_addon_prefs(AddonPreferences):
311316 min = 0.000 ,
312317 max = 1.000
313318 )
314-
319+
315320 def draw (self , context ):
316321
317322 layout = self .layout
318323
319324 layout .label ('Defaults' ,icon = 'PREFERENCES' )
320325
326+ layout .prop (self , 'option_relative' )
321327 layout .prop (self , 'option_ao_map' )
322328 layout .prop (self , 'option_metallic_map' )
323329 layout .prop (self , 'option_metallic' )
@@ -339,6 +345,13 @@ def get_option_metallic_map(self):
339345 def set_option_metallic_map (self , value ):
340346 self ["_option_metallic_map" ] = value
341347
348+ def get_option_relative (self ):
349+ if "_option_relative" not in self :
350+ self ["_option_relative" ]= bpy .context .user_preferences .addons [__name__ ].preferences .option_relative
351+ return self ["_option_relative" ]
352+ def set_option_relative (self , value ):
353+ self ["_option_relative" ] = value
354+
342355 option_ao_map = BoolProperty (
343356 name = "Ambient Occlusion" ,
344357 description = "Add Ambient Occlusion Map via RGB Mix Node" ,
@@ -351,15 +364,156 @@ def set_option_metallic_map(self, value):
351364 get = get_option_metallic_map ,
352365 set = set_option_metallic_map
353366 )
367+ option_relative = BoolProperty (
368+ name = "Relative Paths" ,
369+ description = "Use Relative Paths for images." ,
370+ get = get_option_relative ,
371+ set = set_option_relative
372+ )
354373 textures_path = StringProperty (
355- name = "Textures Path" ,
374+ name = "Auto Textures Path" ,
356375 subtype = 'DIR_PATH' ,
357376 )
358377 option_nodes_type = StringProperty (
359378 name = "Nodes Type" ,
360379 description = "Nodes Type" ,
361380 default = ''
362381 )
382+
383+ class ds_pbr_texture_base_color (bpy .types .Operator ):
384+ bl_idname = "ds_pbr.base_color"
385+ bl_label = "Base Color"
386+ filepath = bpy .props .StringProperty (subtype = "FILE_PATH" )
387+ bl_context = "material"
388+
389+ option_relative = bpy .props .BoolProperty (name = "Relative" )
390+
391+ def execute (self , context ):
392+ _nodes = context .material .node_tree .nodes
393+ if self .option_relative == True :
394+ _filepath = bpy .path .relpath (self .filepath )
395+ else :
396+ _filepath = bpy .path .abspath (self .filepath )
397+ _nodes ['ds_pbr_texture_base_color' ].image = bpy .data .images .load (_filepath )
398+ return {'FINISHED' }
399+
400+ def invoke (self , context , event ):
401+ if not self .filepath and context .material .ds_pbr_material_options .textures_path :
402+ self .filepath = context .material .ds_pbr_material_options .textures_path
403+ self .option_relative = context .material .ds_pbr_material_options .option_relative
404+ context .window_manager .fileselect_add (self )
405+ return {'RUNNING_MODAL' }
406+
407+ class ds_pbr_texture_normal (bpy .types .Operator ):
408+ bl_idname = "ds_pbr.texture_normal"
409+ bl_label = "Normal"
410+ filepath = bpy .props .StringProperty (subtype = "FILE_PATH" )
411+ bl_context = "material"
412+
413+ option_relative = bpy .props .BoolProperty (name = "Relative" )
414+
415+ def execute (self , context ):
416+ _nodes = context .material .node_tree .nodes
417+ if self .option_relative == True :
418+ _filepath = bpy .path .relpath (self .filepath )
419+ else :
420+ _filepath = bpy .path .abspath (self .filepath )
421+ _nodes ['ds_pbr_texture_normal' ].image = bpy .data .images .load (_filepath )
422+ return {'FINISHED' }
423+
424+ def invoke (self , context , event ):
425+ if not self .filepath and context .material .ds_pbr_material_options .textures_path :
426+ self .filepath = context .material .ds_pbr_material_options .textures_path
427+ self .option_relative = context .material .ds_pbr_material_options .option_relative
428+ context .window_manager .fileselect_add (self )
429+ return {'RUNNING_MODAL' }
430+
431+ class ds_pbr_texture_ao (bpy .types .Operator ):
432+ bl_idname = "ds_pbr.texture_ao"
433+ bl_label = "Ambient Occlusion"
434+ filepath = bpy .props .StringProperty (subtype = "FILE_PATH" )
435+ bl_context = "material"
436+
437+ option_relative = bpy .props .BoolProperty (name = "Relative" )
438+
439+ def execute (self , context ):
440+ _nodes = context .material .node_tree .nodes
441+ if self .option_relative == True :
442+ _filepath = bpy .path .relpath (self .filepath )
443+ else :
444+ _filepath = bpy .path .abspath (self .filepath )
445+ _nodes ['ds_pbr_texture_ao' ].image = bpy .data .images .load (_filepath )
446+ return {'FINISHED' }
447+
448+ def invoke (self , context , event ):
449+ if not self .filepath and context .material .ds_pbr_material_options .textures_path :
450+ self .filepath = context .material .ds_pbr_material_options .textures_path
451+ self .option_relative = context .material .ds_pbr_material_options .option_relative
452+ context .window_manager .fileselect_add (self )
453+ return {'RUNNING_MODAL' }
454+
455+ class ds_pbr_texture_metallic (bpy .types .Operator ):
456+ bl_idname = "ds_pbr.texture_metallic"
457+ bl_label = "Metallic"
458+ bl_context = "material"
459+
460+ filepath = bpy .props .StringProperty (subtype = "FILE_PATH" )
461+ option_relative = bpy .props .BoolProperty (name = "Relative" )
462+
463+ def execute (self , context ):
464+ _nodes = context .material .node_tree .nodes
465+ if self .option_relative == True :
466+ _filepath = bpy .path .relpath (self .filepath )
467+ else :
468+ _filepath = bpy .path .abspath (self .filepath )
469+ _nodes ['ds_pbr_texture_metallic' ].image = bpy .data .images .load (_filepath )
470+ return {'FINISHED' }
471+
472+ def invoke (self , context , event ):
473+ if not self .filepath and context .material .ds_pbr_material_options .textures_path :
474+ self .filepath = context .material .ds_pbr_material_options .textures_path
475+ self .option_relative = context .material .ds_pbr_material_options .option_relative
476+ context .window_manager .fileselect_add (self )
477+ return {'RUNNING_MODAL' }
478+
479+ class ds_pbr_texture_roughness (bpy .types .Operator ):
480+ bl_idname = "ds_pbr.texture_roughness"
481+ bl_label = "Roughness"
482+ filepath = bpy .props .StringProperty (subtype = "FILE_PATH" )
483+ bl_context = "material"
484+ option_relative = bpy .props .BoolProperty (name = "Relative" )
485+
486+ def execute (self , context ):
487+ _nodes = context .material .node_tree .nodes
488+ if self .option_relative == True :
489+ _filepath = bpy .path .relpath (self .filepath )
490+ else :
491+ _filepath = bpy .path .abspath (self .filepath )
492+ _nodes ['ds_pbr_texture_roughness' ].image = bpy .data .images .load (_filepath )
493+ return {'FINISHED' }
494+
495+ def invoke (self , context , event ):
496+ if not self .filepath and context .material .ds_pbr_material_options .textures_path :
497+ self .filepath = context .material .ds_pbr_material_options .textures_path
498+ self .option_relative = context .material .ds_pbr_material_options .option_relative
499+ context .window_manager .fileselect_add (self ,)
500+ return {'RUNNING_MODAL' }
501+
502+ class ds_pbr_render_cycles (bpy .types .Operator ):
503+ bl_idname = "ds_pbr.render_cycles"
504+ bl_label = "CYCLES"
505+ bl_context = "scene"
506+ def execute (self , context ):
507+ bpy .context .scene .render .engine = 'CYCLES'
508+ return {'FINISHED' }
509+
510+ class ds_pbr_render_game (bpy .types .Operator ):
511+ bl_idname = "ds_pbr.render_game"
512+ bl_label = "BLENDER_GAME"
513+ bl_context = "scene"
514+ def execute (self , context ):
515+ bpy .context .scene .render .engine = 'BLENDER_GAME'
516+ return {'FINISHED' }
363517
364518class ds_pbr_material (Panel ):
365519
@@ -385,51 +539,93 @@ def draw(self, context):
385539
386540 layout .label ('Textures' ,icon = 'IMASEL' )
387541
388- split = layout .split (0.30 )
389- split .label ('Base Color' )
390- split .template_ID (_nodes ['ds_pbr_texture_base_color' ], 'image' , open = 'image.open' )
391-
392- if (_ds_pbr_material_options .option_metallic_map == True ):
542+ row = layout .row (align = True )
543+ col = row .split (percentage = 0.3 )
544+ col .label (ds_pbr_texture_base_color .bl_label )
545+ if _nodes ['ds_pbr_texture_base_color' ].image :
546+ col .template_ID (_nodes ['ds_pbr_texture_base_color' ], "image" , "image.open" )
547+ else :
548+ col .operator (ds_pbr_texture_base_color .bl_idname , icon = "FILE_FOLDER" ,text = "Select Image" )
393549
394- if ( 'ds_pbr_texture_metallic' in _nodes ):
550+ if ( _ds_pbr_material_options . option_metallic_map == True and 'ds_pbr_texture_metallic' in _nodes ):
395551
396- split = layout .split (0.30 )
397- split .label ('Metallic' )
398- split .template_ID (_nodes ['ds_pbr_texture_metallic' ], 'image' , open = 'image.open' )
552+ row = layout .row (align = True )
553+ col = row .split (percentage = 0.3 )
554+ col .label (ds_pbr_texture_metallic .bl_label )
555+
556+ if _nodes ['ds_pbr_texture_metallic' ].image :
557+ col .template_ID (_nodes ['ds_pbr_texture_metallic' ], "image" , "image.open" )
558+ else :
559+ col .operator (ds_pbr_texture_metallic .bl_idname , icon = "FILE_FOLDER" ,text = "Select Image" )
399560
400561 if (_ds_pbr_material_options .option_ao_map == True ):
401562
563+ row = layout .row (align = True )
564+ col = row .split (percentage = 0.3 )
565+ col .label (ds_pbr_texture_ao .bl_label )
402566 if ('ds_pbr_texture_ao' in _nodes ):
567+ if _nodes ['ds_pbr_texture_ao' ].image :
568+ col .template_ID (_nodes ['ds_pbr_texture_ao' ], "image" , "image.open" )
569+ else :
570+ col .operator (ds_pbr_texture_ao .bl_idname , icon = "FILE_FOLDER" ,text = "Select Image" )
571+
572+ row = layout .row (align = True )
573+ col = row .split (percentage = 0.3 )
574+ col .label (ds_pbr_texture_normal .bl_label )
575+ if _nodes ['ds_pbr_texture_normal' ].image :
576+ col .template_ID (_nodes ['ds_pbr_texture_normal' ], "image" , "image.open" )
577+ else :
578+ col .operator (ds_pbr_texture_normal .bl_idname , icon = "FILE_FOLDER" ,text = "Select Image" )
579+
580+ row = layout .row (align = True )
581+ col = row .split (percentage = 0.3 )
582+ col .label (ds_pbr_texture_roughness .bl_label )
583+ if _nodes ['ds_pbr_texture_roughness' ].image :
584+ col .template_ID (_nodes ['ds_pbr_texture_roughness' ], "image" , "image.open" )
585+ else :
586+ col .operator (ds_pbr_texture_roughness .bl_idname , icon = "FILE_FOLDER" ,text = "Select Image" )
587+
588+ layout .label ('Options' ,icon = 'NODETREE' )
403589
404- split = layout .split (0.30 )
405- split .label ('Ambient Occulussion' )
406- split .template_ID (_nodes ['ds_pbr_texture_ao' ], 'image' , open = 'image.open' )
407-
408- split = layout .split (0.30 )
409- split .label ('Normal' )
410- split .template_ID (_nodes ['ds_pbr_texture_normal' ], 'image' , open = 'image.open' )
411-
412- split = layout .split (0.30 )
413- split .label ('Roughness' )
414- split .template_ID (_nodes ['ds_pbr_texture_roughness' ], 'image' , open = 'image.open' )
415-
416- layout .label ('Auto Textures' ,icon = 'IMASEL' )
417590 layout .prop (_ds_pbr_material_options , "textures_path" )
591+ layout .prop (_ds_pbr_material_options , "option_relative" )
592+
593+ if not bpy .data .filepath and _ds_pbr_material_options .option_relative == True :
594+ layout .label ('Blender file not saved. Required for relative paths.' ,icon = 'INFO' )
418595
419596 layout .label ('Optional Nodes' ,icon = 'NODETREE' )
420597
421598 layout .prop (_ds_pbr_material_options , "option_ao_map" )
422599 layout .prop (_ds_pbr_material_options , "option_metallic_map" )
600+
601+
602+ if (bpy .context .scene .render .engine != 'CYCLES' and bpy .context .scene .render .engine != 'BLENDER_GAME' ):
603+
604+ layout .label ('Cycles or Blender Game Render Engine required for PBR' ,'INFO' )
605+ row = layout .row (align = True )
606+ col = row .split (percentage = 0.5 )
607+ col .operator ('ds_pbr.render_cycles' )
608+ col .operator ('ds_pbr.render_game' )
423609
424- layout .operator ("ds_pbr.nodes_metallic_roughness" )
425- layout .operator ("ds_pbr.nodes_specular_gloss" )
610+ row = layout .row (align = True )
611+ col = row .split (percentage = 0.5 )
612+ col .operator ("ds_pbr.nodes_metallic_roughness" )
613+ col .operator ("ds_pbr.nodes_specular_gloss" )
426614
427615 layout .separator ()
428616
429617def register ():
430618
431619 from bpy .utils import register_class
432620
621+ register_class (ds_pbr_render_cycles )
622+ register_class (ds_pbr_render_game )
623+ register_class (ds_pbr_texture_base_color )
624+ register_class (ds_pbr_texture_normal )
625+ register_class (ds_pbr_texture_ao )
626+ register_class (ds_pbr_texture_metallic )
627+ register_class (ds_pbr_texture_roughness )
628+
433629 register_class (ds_pbr_addon_prefs )
434630 register_class (ds_pbr_material_options )
435631 register_class (ds_pbr_nodes_remove )
@@ -444,6 +640,14 @@ def unregister():
444640
445641 from bpy .utils import unregister_class
446642
643+ unregister_class (ds_pbr_render_cycles )
644+ unregister_class (ds_pbr_render_game )
645+ unregister_class (ds_pbr_texture_base_color )
646+ unregister_class (ds_pbr_texture_normal )
647+ unregister_class (ds_pbr_texture_ao )
648+ unregister_class (ds_pbr_texture_metallic )
649+ unregister_class (ds_pbr_texture_roughness )
650+
447651 unregister_class (ds_pbr_addon_prefs )
448652 unregister_class (ds_pbr_material_options )
449653 unregister_class (ds_pbr_nodes_remove )
0 commit comments