2 "name": "Texture Paint Layer Manager",
3 "author": "Michael Wiliamson",
6 "location": "Texture Paint > Properties > Texture Paint Layers Panels",
7 "description": "Adds a layer manager for image based texture slots in paint and quick add layer tools",
9 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/Texture_paint_layers",
10 "tracker_url": "https://developer.blender.org/T26789",
15 from bpy
.props
import*
17 from bpy_extras
.io_utils
import ImportHelper
20 #-------------------------------------------
22 def load_a_brush(context
, filepath
):
23 if os
.path
.isdir(filepath
):
29 fn
= bpy
.path
.display_name_from_filepath(filepath
)
30 #create image and load...
31 img
= bpy
.data
.images
.load(filepath
)
32 img
.use_fake_user
=True
35 tex
= bpy
.data
.textures
.new(name
=fn
, type='IMAGE')
36 tex
.use_fake_user
=True
37 #tex.use_calculate_alpha = True
39 #link the img to the texture
43 print(f
,'is not image?')
50 class load_single_brush(bpy
.types
.Operator
, ImportHelper
):
51 """Load an image as a brush texture"""
52 bl_idname
= "texture.load_single_brush"
53 bl_label
= "Load Image as Brush"
57 def poll(cls
, context
):
58 return context
.active_object
!= None
60 def execute(self
, context
):
61 return load_a_brush(context
, self
.filepath
)
63 #-------------------------------------------
65 def loadbrushes(context
, filepath
):
66 if os
.path
.isdir(filepath
):
70 #is a file, find parent directory
71 li
= filepath
.split(os
.sep
)
72 directory
= filepath
.rstrip(li
[-1])
75 files
= os
.listdir(directory
)
79 #create image and load...
80 img
= bpy
.data
.images
.load(filepath
= directory
+os
.sep
+ f
)
81 img
.use_fake_user
=True
84 tex
= bpy
.data
.textures
.new(name
=fn
, type='IMAGE')
85 tex
.use_fake_user
=True
86 #tex.use_calculate_alpha = True
88 #link the img to the texture
92 print(f
,'is not image?')
99 class ImportBrushes(bpy
.types
.Operator
, ImportHelper
):
100 """Load a directory of images as brush textures"""
101 bl_idname
= "texture.load_brushes"
102 bl_label
= "Load brushes directory"
106 def poll(cls
, context
):
107 return context
.active_object
!= None
109 def execute(self
, context
):
110 return loadbrushes(context
, self
.filepath
)
112 #-------------------------------------------------------------------
114 class OBJECT_PT_LoadBrushes(bpy
.types
.Panel
):
115 bl_label
= "Load Brush images"
116 bl_space_type
= "VIEW_3D"
117 bl_region_type
= "TOOLS"
118 #bl_context = "texturepaint"
121 def poll(cls
, context
):
122 return (context
.sculpt_object
or context
.image_paint_object
)
124 def draw(self
, context
):
127 layout
.operator('texture.load_brushes')
128 layout
.operator('texture.load_single_brush')
131 #======================================================================
137 class OBJECT_PT_Texture_paint_layers(bpy
.types
.Panel
):
138 bl_label
= "Texture Paint Layers"
139 bl_space_type
= "VIEW_3D"
140 bl_region_type
= "UI"
141 #bl_context = "texturepaint"
144 def poll(cls
, context
):
145 return (context
.image_paint_object
)
147 def draw(self
, context
):
150 ob
= bpy
.context
.image_paint_object
152 mat
= ob
.active_material
155 row
.label(' Add a Material first!', icon
= 'ERROR')
158 row
.template_list("UI_UL_list", "texture_paint_layers", ob
, "material_slots", ob
,
159 "active_material_index", rows
=2 )
161 #list Paintable textures
162 #TODO add filter for channel type
164 for t
in mat
.texture_slots
:
167 if t
.texture
.type =='IMAGE':
168 row
= layout
.row(align
= True)
169 if t
.texture
== mat
.active_texture
:
173 row
.operator('object.set_active_paint_layer',
174 text
= "", icon
= ai
).tex_index
=i
175 row
.prop(t
.texture
,'name', text
= "")
180 ic
= 'RESTRICT_VIEW_OFF'
182 ic
= 'RESTRICT_VIEW_ON'
183 row
.prop(t
,'use', text
= "",icon
= ic
)
192 ts
= mat
.texture_slots
[mat
.active_texture_index
]
200 col
= layout
.column(align
=True)
201 col
.label('Active Properties:', icon
= 'BRUSH_DATA')
203 #use if rather than elif... can be mapped to multiple things
204 if ts
.use_map_diffuse
:
205 col
.prop(ts
,'diffuse_factor', slider
= True)
206 if ts
.use_map_color_diffuse
:
207 col
.prop(ts
,'diffuse_color_factor', slider
= True)
209 col
.prop(ts
,'alpha_factor', slider
= True)
210 if ts
.use_map_translucency
:
211 col
.prop(ts
,'translucency_factor', slider
= True)
212 if ts
.use_map_specular
:
213 col
.prop(ts
,'specular_factor', slider
= True)
214 if ts
.use_map_color_spec
:
215 col
.prop(ts
,'specular_color_factor', slider
= True)
216 if ts
.use_map_hardness
:
217 col
.prop(ts
,'hardness_factor', slider
= True)
219 if ts
.use_map_normal
:
220 col
.prop(ts
,'normal_factor', slider
= True)
222 col
.prop(ts
,'warp_factor', slider
= True)
223 if ts
.use_map_displacement
:
224 col
.prop(ts
,'displacement_factor', slider
= True)
226 if ts
.use_map_ambient
:
227 col
.prop(ts
,'ambient_factor', slider
= True)
229 col
.prop(ts
,'emit_factor', slider
= True)
230 if ts
.use_map_mirror
:
231 col
.prop(ts
,'mirror_factor', slider
= True)
232 if ts
.use_map_raymir
:
233 col
.prop(ts
,'raymir_factor', slider
= True)
236 col
.prop(ts
,'blend_type',text
='')
240 row
.label('No paint layers in material', icon
= 'ERROR')
246 # row.label('WIP: Use the X to delete!:')
248 # row.template_ID(mat, "active_texture", new="texture.new")
251 class OBJECT_PT_Texture_paint_add(bpy
.types
.Panel
):
252 bl_label
= "Add Paint Layers"
253 bl_space_type
= "VIEW_3D"
254 bl_region_type
= "UI"
255 #bl_context = "texturepaint"
258 def poll(cls
, context
):
259 return (context
.image_paint_object
)
261 def draw(self
, context
):
264 ob
= bpy
.context
.image_paint_object
266 mat
= ob
.active_material
269 col
= layout
.column(align
=True)
271 col
.operator('object.add_paint_layer',
272 text
= "Add Color").ttype
= 'COLOR'
273 col
.operator('object.add_paint_layer',
274 text
= "Add Bump").ttype
= 'NORMAL'
276 col
= layout
.column(align
=True)
277 col
.operator('object.add_paint_layer',
278 text
= "Add Specular").ttype
= 'SPECULAR'
279 col
.operator('object.add_paint_layer',
280 text
= "Add Spec Col").ttype
= 'SPEC_COL'
281 col
.operator('object.add_paint_layer',
282 text
= "Add Hardness").ttype
= 'HARDNESS'
284 col
= layout
.column(align
=True)
285 col
.operator('object.add_paint_layer',
286 text
= "Add Alpha").ttype
= 'ALPHA'
287 col
.operator('object.add_paint_layer',
288 text
= "Add Translucency").ttype
= 'TRANSLUCENCY'
290 # col = layout.column(align =True)
291 # col.operator('object.add_paint_layer',
292 # text = "Add Mirror").ttype = 'MIRROR'
293 # col.operator('object.add_paint_layer',
294 # text = "Add Ray Mirror").ttype = 'RAY_MIRROR'
296 col
= layout
.column(align
=True)
297 col
.operator('object.add_paint_layer',
298 text
= "Add Emit").ttype
= 'EMIT'
299 col
.operator('object.add_paint_layer',
300 text
= "Add Diffuse").ttype
= 'DIFFUSE'
301 col
.operator('object.add_paint_layer',
302 text
= "Add Ambient").ttype
= 'AMBIENT'
305 layout
.label(' Add a Material first!', icon
= 'ERROR')
309 def main(context
,tn
):
310 #tn is the index of the texture in the active material
311 ob
= context
.active_object
313 mat
= ob
.active_material
314 mat
.active_texture_index
= tn
315 ts
= mat
.texture_slots
[tn
]
317 #make sure it's visible
321 if not me
.uv_textures
:
322 bpy
.ops
.mesh
.uv_texture_add()
324 # texture Slot uses UVs?
325 if ts
.texture_coords
== 'UV':
327 uvtex
= me
.uv_textures
[ts
.uv_layer
]
330 uvtex
= me
.uv_textures
.active
331 me
.uv_textures
.active
= uvtex
333 ts
.texture_coords
='UV'
334 uvtex
= me
.uv_textures
.active
337 uvtex
= uvtex
.data
.values()
340 #get image from texture slot
341 img
= ts
.texture
.image
344 m_id
= ob
.active_material_index
347 for f
in me
.polygons
:
348 if f
.material_index
== m_id
:
349 uvtex
[f
.index
].image
= img
354 for f
in me
.polygons
:
355 if f
.material_index
== m_id
:
356 uvtex
[f
.index
].image
= None
366 class set_active_paint_layer(bpy
.types
.Operator
):
368 bl_idname
= "object.set_active_paint_layer"
369 bl_label
= "set_active_paint_layer"
370 tex_index
= IntProperty(name
= 'tex_index',
371 description
= "", default
= 0)
374 def poll(cls
, context
):
375 return context
.active_object
!= None
377 def execute(self
, context
):
384 def add_image_kludge(iname
= 'grey', iwidth
= 256, iheight
= 256,
385 icolor
= (0.5,0.5,0.5,1.0), nfloat
= False):
386 #evil kludge to get index of new image created using bpy.ops
387 #store current images
389 for i
in bpy
.data
.images
:
395 bpy
.ops
.image
.new(name
=iname
,width
=iwidth
,height
=iheight
,
396 color
= icolor
, float = nfloat
)
398 #find its creation index
400 for i
in bpy
.data
.images
:
402 return(bpy
.data
.images
[it
])
407 def add_paint(context
, size
=2048, typ
= 'NORMAL'):
409 ob
= bpy
.context
.object
410 mat
= ob
.active_material
411 ts
= mat
.texture_slots
.add()
415 color
=(0.5,0.5,0.5,1.0)
420 color
= (1.0,1.0,1.0,0.0)
424 color
= (1.0,1.0,1.0,0.0)
426 color
=(0.0,0.0,0.0,1.0)
427 iname
= typ
.capitalize()
429 # bn = bpy.context.blend_data.filepath.split(bpy.utils._os.sep)[-1]
430 # bn = bn.replace('.blend', '')
433 iname
= bn
+'_' + iname
435 tex
= bpy
.data
.textures
.new(name
= iname
, type = 'IMAGE')
437 img
= add_image_kludge(iname
= typ
,
438 iwidth
= size
,iheight
= size
, icolor
= color
, nfloat
= ifloat
)
442 ts
.use_map_color_diffuse
=True
445 elif typ
== 'NORMAL':
446 ts
.use_map_normal
= True
447 ts
.use_map_color_diffuse
=False
448 ts
.normal_factor
= -1
449 ts
.bump_method
='BUMP_MEDIUM_QUALITY'
450 ts
.bump_objectspace
='BUMP_OBJECTSPACE'
452 elif typ
== 'SPECULAR':
453 ts
.use_map_specular
= True
454 ts
.use_map_color_diffuse
=False
455 ts
.use_rgb_to_intensity
= True
456 #ts.blend_type = 'MULTIPLY'
459 ts
.use_map_emit
= True
460 ts
.use_map_color_diffuse
=False
461 ts
.use_rgb_to_intensity
= True
464 mat
.use_transparency
= True
465 ts
.use_map_alpha
= True
466 ts
.use_map_color_diffuse
=False
467 ts
.use_rgb_to_intensity
= True
468 ts
.blend_type
= 'MULTIPLY'
470 elif typ
== 'SPEC_COL':
471 ts
.use_map_color_spec
= True
472 ts
.use_map_color_diffuse
=False
473 ts
.use_rgb_to_intensity
= True
475 elif typ
== 'HARDNESS':
476 ts
.use_map_hardness
= True
477 ts
.use_map_color_diffuse
=False
478 ts
.use_rgb_to_intensity
= True
480 elif typ
== 'DIFFUSE':
481 ts
.use_map_diffuse
= True
482 ts
.use_map_color_diffuse
=False
483 ts
.use_rgb_to_intensity
= True
485 elif typ
== 'TRANSLUCENCY':
486 ts
.use_map_translucency
= True
487 ts
.use_map_color_diffuse
=False
488 ts
.use_rgb_to_intensity
= True
490 elif typ
== 'AMBIENT':
491 ts
.use_map_ambient
= True
492 ts
.use_map_color_diffuse
=False
493 ts
.use_rgb_to_intensity
= True
495 elif typ
== 'MIRROR':
496 ts
.use_map_mirror
= True
497 ts
.use_map_color_diffuse
=False
498 ts
.use_rgb_to_intensity
= True
500 elif typ
== 'RAY_MIRROR':
501 mat
.raytrace_mirror
.use
= True
502 ts
.use_map_ray_mirror
= True
503 ts
.use_map_color_diffuse
=False
504 ts
.use_rgb_to_intensity
= True
506 #set new texture slot to active
509 for t
in mat
.texture_slots
:
516 mat
.active_texture_index
= ts_index
518 #set the texfaces using this material.
519 main(context
,ts_index
)
525 class add_paint_layer(bpy
.types
.Operator
):
527 bl_idname
= "object.add_paint_layer"
528 bl_label
= "Add Paint Layer"
529 ttype
= StringProperty(name
='ttype',default
='NORMAL')
532 def poll(cls
, context
):
533 return context
.active_object
!= None
535 def execute(self
, context
):
537 add_paint(context
,typ
= ttype
)
543 #----------------------------------------------
544 def save_painted(ts
):
545 #generated images don't have a path
546 #so don't get saved with "save_dirty"
547 #ts is a texture slot object.
549 sep
= bpy
.utils
._os
.sep
551 if ts
.texture
.type =='IMAGE':
553 if i
.source
=='GENERATED':
556 if i
.file_format
=='PNG':
558 elif i
.file_format
=='TARGA':
561 bpy
.context
.scene
.render
.image_settings
.color_mode
= 'RGBA'
562 fp
= bpy
.path
.abspath('//textures' + sep
+ name
)
566 if bpy
.context
.user_preferences
.filepaths
.use_relative_paths
:
567 # can't always find the relative path (between drive letters on windows)
569 i
.filepath
= bpy
.path
.relpath(fp
)
576 print("something wrong with", fp
)
577 #THAT'S THE GENERATED FILES saved, pathed and reloaded
578 #now save other painted textures
579 bpy
.ops
.image
.save_dirty()
583 def save_active_paint():
584 #for materials in current object
585 ob
= bpy
.context
.object
586 for m
in ob
.material_slots
:
587 for ts
in m
.material
.texture_slots
:
591 def save_all_paint():
593 for m
in bpy
.data
.materials
:
594 for ts
in m
.texture_slots
:
599 class save_all_generated(bpy
.types
.Operator
):
600 """Saves painted layers to disc"""
601 bl_idname
= "paint.save_all_generated"
603 bl_label
= "SAVE PAINT LAYERS"
607 def poll(cls
, context
):
608 return context
.active_object
!= None
610 def execute(self
, context
):
611 return save_active_paint()
616 #-----------------------------------
617 class OBJECT_PT_SavePainted(bpy
.types
.Panel
):
618 bl_label
= "Save All Painted"
619 bl_space_type
= "VIEW_3D"
620 bl_region_type
= "UI"
621 #bl_context = "texturepaint"
624 def poll(cls
, context
):
625 return (context
.image_paint_object
)
627 def draw(self
, context
):
628 self
.layout
.operator('paint.save_all_generated')
631 bpy
.utils
.register_module(__name__
)
634 bpy
.utils
.unregister_module(__name__
)
636 if __name__
== "__main__":