FBX: reformat props.
[blender-addons.git] / texture_paint_layer_manager.py
blob8f59ba8924d854eb01abe8862c89fe5389d1b155
1 bl_info = {
2 "name": "Texture Paint Layer Manager",
3 "author": "Michael Wiliamson",
4 "version": (1, 0),
5 "blender": (2, 57, 0),
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",
8 "warning": "",
9 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
10 "Scripts/3D_interaction/Texture_paint_layers",
11 "category": "Paint",
15 import bpy
16 from bpy.props import*
17 import os
18 from bpy_extras.io_utils import ImportHelper
21 #-------------------------------------------
23 def load_a_brush(context, filepath):
24 if os.path.isdir(filepath):
25 return
27 else:
29 try:
30 fn = bpy.path.display_name_from_filepath(filepath)
31 #create image and load...
32 img = bpy.data.images.load(filepath)
33 img.use_fake_user =True
35 #create a texture
36 tex = bpy.data.textures.new(name =fn, type='IMAGE')
37 tex.use_fake_user =True
38 #tex.use_calculate_alpha = True
40 #link the img to the texture
41 tex.image = img
43 except:
44 print(f,'is not image?')
46 return {'FINISHED'}
51 class load_single_brush(bpy.types.Operator, ImportHelper):
52 """Load an image as a brush texture"""
53 bl_idname = "texture.load_single_brush"
54 bl_label = "Load Image as Brush"
57 @classmethod
58 def poll(cls, context):
59 return context.active_object != None
61 def execute(self, context):
62 return load_a_brush(context, self.filepath)
64 #-------------------------------------------
66 def loadbrushes(context, filepath):
67 if os.path.isdir(filepath):
68 directory = filepath
70 else:
71 #is a file, find parent directory
72 li = filepath.split(os.sep)
73 directory = filepath.rstrip(li[-1])
76 files = os.listdir(directory)
77 for f in files:
78 try:
79 fn = f[3:]
80 #create image and load...
81 img = bpy.data.images.load(filepath = directory +os.sep + f)
82 img.use_fake_user =True
84 #create a texture
85 tex = bpy.data.textures.new(name =fn, type='IMAGE')
86 tex.use_fake_user =True
87 #tex.use_calculate_alpha = True
89 #link the img to the texture
90 tex.image = img
92 except:
93 print(f,'is not image?')
94 continue
95 return {'FINISHED'}
100 class ImportBrushes(bpy.types.Operator, ImportHelper):
101 """Load a directory of images as brush textures"""
102 bl_idname = "texture.load_brushes"
103 bl_label = "Load brushes directory"
106 @classmethod
107 def poll(cls, context):
108 return context.active_object != None
110 def execute(self, context):
111 return loadbrushes(context, self.filepath)
113 #-------------------------------------------------------------------
115 class OBJECT_PT_LoadBrushes(bpy.types.Panel):
116 bl_label = "Load Brush images"
117 bl_space_type = "VIEW_3D"
118 bl_region_type = "TOOLS"
119 #bl_context = "texturepaint"
121 @classmethod
122 def poll(cls, context):
123 return (context.sculpt_object or context.image_paint_object)
125 def draw(self, context):
126 layout = self.layout
128 layout.operator('texture.load_brushes')
129 layout.operator('texture.load_single_brush')
132 #======================================================================
138 class OBJECT_PT_Texture_paint_layers(bpy.types.Panel):
139 bl_label = "Texture Paint Layers"
140 bl_space_type = "VIEW_3D"
141 bl_region_type = "UI"
142 #bl_context = "texturepaint"
144 @classmethod
145 def poll(cls, context):
146 return (context.image_paint_object)
148 def draw(self, context):
149 layout = self.layout
151 ob = bpy.context.image_paint_object
152 if ob:
153 mat = ob.active_material
154 if not mat:
155 row = layout.row()
156 row.label(' Add a Material first!', icon = 'ERROR')
157 else:
158 row = layout.row()
159 row.template_list("UI_UL_list", "texture_paint_layers", ob, "material_slots", ob,
160 "active_material_index", rows=2 )
162 #list Paintable textures
163 #TODO add filter for channel type
164 i = -1
165 for t in mat.texture_slots:
166 i+=1
167 try:
168 if t.texture.type =='IMAGE':
169 row = layout.row(align= True)
170 if t.texture == mat.active_texture:
171 ai = 'BRUSH_DATA'
172 else:
173 ai = 'BLANK1'
174 row.operator('object.set_active_paint_layer',
175 text = "", icon = ai).tex_index =i
176 row.prop(t.texture,'name', text = "")
179 #Visibility
180 if t.use:
181 ic = 'RESTRICT_VIEW_OFF'
182 else:
183 ic = 'RESTRICT_VIEW_ON'
184 row.prop(t,'use', text = "",icon = ic)
185 except:
186 continue
193 ts = mat.texture_slots[mat.active_texture_index]
195 if ts:
196 row = layout.row()
201 col = layout.column(align =True)
202 col.label('Active Properties:', icon = 'BRUSH_DATA')
204 #use if rather than elif... can be mapped to multiple things
205 if ts.use_map_diffuse:
206 col.prop(ts,'diffuse_factor', slider = True)
207 if ts.use_map_color_diffuse:
208 col.prop(ts,'diffuse_color_factor', slider = True)
209 if ts.use_map_alpha:
210 col.prop(ts,'alpha_factor', slider = True)
211 if ts.use_map_translucency:
212 col.prop(ts,'translucency_factor', slider = True)
213 if ts.use_map_specular:
214 col.prop(ts,'specular_factor', slider = True)
215 if ts.use_map_color_spec:
216 col.prop(ts,'specular_color_factor', slider = True)
217 if ts.use_map_hardness:
218 col.prop(ts,'hardness_factor', slider = True)
220 if ts.use_map_normal:
221 col.prop(ts,'normal_factor', slider = True)
222 if ts.use_map_warp:
223 col.prop(ts,'warp_factor', slider = True)
224 if ts.use_map_displacement:
225 col.prop(ts,'displacement_factor', slider = True)
227 if ts.use_map_ambient:
228 col.prop(ts,'ambient_factor', slider = True)
229 if ts.use_map_emit:
230 col.prop(ts,'emit_factor', slider = True)
231 if ts.use_map_mirror:
232 col.prop(ts,'mirror_factor', slider = True)
233 if ts.use_map_raymir:
234 col.prop(ts,'raymir_factor', slider = True)
237 col.prop(ts,'blend_type',text='')
239 else:
240 row=layout.row()
241 row.label('No paint layers in material', icon = 'ERROR')
244 # row = layout.row()
245 # row.label('')
246 # row = layout.row()
247 # row.label('WIP: Use the X to delete!:')
248 # row = layout.row()
249 # row.template_ID(mat, "active_texture", new="texture.new")
252 class OBJECT_PT_Texture_paint_add(bpy.types.Panel):
253 bl_label = "Add Paint Layers"
254 bl_space_type = "VIEW_3D"
255 bl_region_type = "UI"
256 #bl_context = "texturepaint"
258 @classmethod
259 def poll(cls, context):
260 return (context.image_paint_object)
262 def draw(self, context):
263 layout = self.layout
265 ob = bpy.context.image_paint_object
266 if ob:
267 mat = ob.active_material
269 if mat:
270 col = layout.column(align =True)
272 col.operator('object.add_paint_layer',
273 text = "Add Color").ttype = 'COLOR'
274 col.operator('object.add_paint_layer',
275 text = "Add Bump").ttype = 'NORMAL'
277 col = layout.column(align =True)
278 col.operator('object.add_paint_layer',
279 text = "Add Specular").ttype = 'SPECULAR'
280 col.operator('object.add_paint_layer',
281 text = "Add Spec Col").ttype = 'SPEC_COL'
282 col.operator('object.add_paint_layer',
283 text = "Add Hardness").ttype = 'HARDNESS'
285 col = layout.column(align =True)
286 col.operator('object.add_paint_layer',
287 text = "Add Alpha").ttype = 'ALPHA'
288 col.operator('object.add_paint_layer',
289 text = "Add Translucency").ttype = 'TRANSLUCENCY'
291 # col = layout.column(align =True)
292 # col.operator('object.add_paint_layer',
293 # text = "Add Mirror").ttype = 'MIRROR'
294 # col.operator('object.add_paint_layer',
295 # text = "Add Ray Mirror").ttype = 'RAY_MIRROR'
297 col = layout.column(align =True)
298 col.operator('object.add_paint_layer',
299 text = "Add Emit").ttype = 'EMIT'
300 col.operator('object.add_paint_layer',
301 text = "Add Diffuse").ttype = 'DIFFUSE'
302 col.operator('object.add_paint_layer',
303 text = "Add Ambient").ttype = 'AMBIENT'
305 else:
306 layout.label(' Add a Material first!', icon = 'ERROR')
310 def main(context,tn):
311 #tn is the index of the texture in the active material
312 ob = context.active_object
313 me = ob.data
314 mat = ob.active_material
315 mat.active_texture_index = tn
316 ts = mat.texture_slots[tn]
318 #make sure it's visible
319 ts.use = True
321 #Mesh use UVs?
322 if not me.uv_textures:
323 bpy.ops.mesh.uv_texture_add()
325 # texture Slot uses UVs?
326 if ts.texture_coords == 'UV':
327 if ts.uv_layer:
328 uvtex = me.uv_textures[ts.uv_layer]
330 else:
331 uvtex = me.uv_textures.active
332 me.uv_textures.active= uvtex
333 else:
334 ts.texture_coords ='UV'
335 uvtex = me.uv_textures.active
338 uvtex = uvtex.data.values()
341 #get image from texture slot
342 img = ts.texture.image
344 #get material index
345 m_id = ob.active_material_index
347 if img:
348 for f in me.polygons:
349 if f.material_index == m_id:
350 uvtex[f.index].image = img
354 else:
355 for f in me.polygons:
356 if f.material_index == m_id:
357 uvtex[f.index].image = None
359 me.update()
367 class set_active_paint_layer(bpy.types.Operator):
368 """"""
369 bl_idname = "object.set_active_paint_layer"
370 bl_label = "set_active_paint_layer"
371 tex_index = IntProperty(name = 'tex_index',
372 description = "", default = 0)
374 @classmethod
375 def poll(cls, context):
376 return context.active_object != None
378 def execute(self, context):
379 tn = self.tex_index
380 main(context, tn)
381 return {'FINISHED'}
385 def add_image_kludge(iname = 'grey', iwidth = 256, iheight = 256,
386 icolor = (0.5,0.5,0.5,1.0), nfloat = False):
387 #evil kludge to get index of new image created using bpy.ops
388 #store current images
389 tl =[]
390 for i in bpy.data.images:
391 tl.append(i.name)
394 #create a new image
396 bpy.ops.image.new(name =iname,width =iwidth,height =iheight,
397 color = icolor, float = nfloat)
399 #find its creation index
400 it = 0
401 for i in bpy.data.images:
402 if i.name not in tl:
403 return(bpy.data.images[it])
404 break
405 it += 1
408 def add_paint(context, size =2048, typ = 'NORMAL'):
410 ob = bpy.context.object
411 mat = ob.active_material
412 ts = mat.texture_slots.add()
413 ifloat = False
415 if typ =='NORMAL':
416 color =(0.5,0.5,0.5,1.0)
417 iname = 'Bump'
418 ifloat = True
419 elif typ =='COLOR':
420 iname ='Color'
421 color = (1.0,1.0,1.0,0.0)
423 elif typ =='ALPHA':
424 iname ='Alpha'
425 color = (1.0,1.0,1.0,0.0)
426 else:
427 color =(0.0,0.0,0.0,1.0)
428 iname = typ.capitalize()
430 # bn = bpy.context.blend_data.filepath.split(bpy.utils._os.sep)[-1]
431 # bn = bn.replace('.blend', '')
432 bn = ob.name
434 iname = bn +'_' + iname
436 tex = bpy.data.textures.new(name = iname, type = 'IMAGE')
437 ts.texture = tex
438 img = add_image_kludge(iname = typ,
439 iwidth = size,iheight = size, icolor= color, nfloat = ifloat)
440 tex.image = img
442 if typ == 'COLOR':
443 ts.use_map_color_diffuse =True
446 elif typ == 'NORMAL':
447 ts.use_map_normal = True
448 ts.use_map_color_diffuse =False
449 ts.normal_factor = -1
450 ts.bump_method='BUMP_MEDIUM_QUALITY'
451 ts.bump_objectspace='BUMP_OBJECTSPACE'
453 elif typ == 'SPECULAR':
454 ts.use_map_specular = True
455 ts.use_map_color_diffuse =False
456 ts.use_rgb_to_intensity = True
457 #ts.blend_type = 'MULTIPLY'
459 elif typ == 'EMIT':
460 ts.use_map_emit = True
461 ts.use_map_color_diffuse =False
462 ts.use_rgb_to_intensity = True
464 elif typ == 'ALPHA':
465 mat.use_transparency = True
466 ts.use_map_alpha = True
467 ts.use_map_color_diffuse =False
468 ts.use_rgb_to_intensity = True
469 ts.blend_type = 'MULTIPLY'
471 elif typ == 'SPEC_COL':
472 ts.use_map_color_spec = True
473 ts.use_map_color_diffuse =False
474 ts.use_rgb_to_intensity = True
476 elif typ == 'HARDNESS':
477 ts.use_map_hardness = True
478 ts.use_map_color_diffuse =False
479 ts.use_rgb_to_intensity = True
481 elif typ == 'DIFFUSE':
482 ts.use_map_diffuse = True
483 ts.use_map_color_diffuse =False
484 ts.use_rgb_to_intensity = True
486 elif typ == 'TRANSLUCENCY':
487 ts.use_map_translucency = True
488 ts.use_map_color_diffuse =False
489 ts.use_rgb_to_intensity = True
491 elif typ == 'AMBIENT':
492 ts.use_map_ambient = True
493 ts.use_map_color_diffuse =False
494 ts.use_rgb_to_intensity = True
496 elif typ == 'MIRROR':
497 ts.use_map_mirror = True
498 ts.use_map_color_diffuse =False
499 ts.use_rgb_to_intensity = True
501 elif typ == 'RAY_MIRROR':
502 mat.raytrace_mirror.use = True
503 ts.use_map_ray_mirror = True
504 ts.use_map_color_diffuse =False
505 ts.use_rgb_to_intensity = True
507 #set new texture slot to active
508 i = 0
509 ts_index = None
510 for t in mat.texture_slots:
511 if t == ts:
513 ts_index = i
514 break
515 i += 1
516 if ts_index != None:
517 mat.active_texture_index = ts_index
519 #set the texfaces using this material.
520 main(context,ts_index)
526 class add_paint_layer(bpy.types.Operator):
527 """"""
528 bl_idname = "object.add_paint_layer"
529 bl_label = "Add Paint Layer"
530 ttype = StringProperty(name ='ttype',default ='NORMAL')
532 @classmethod
533 def poll(cls, context):
534 return context.active_object != None
536 def execute(self, context):
537 ttype = self.ttype
538 add_paint(context,typ= ttype)
539 return {'FINISHED'}
544 #----------------------------------------------
545 def save_painted(ts):
546 #generated images don't have a path
547 #so don't get saved with "save_dirty"
548 #ts is a texture slot object.
550 sep = bpy.utils._os.sep
551 if ts:
552 if ts.texture.type =='IMAGE':
553 i = ts.texture.image
554 if i.source =='GENERATED':
555 if i.is_dirty:
556 name = ts.name
557 if i.file_format =='PNG':
558 name = name + '.png'
559 elif i.file_format =='TARGA':
560 name = name +'.tga'
562 bpy.context.scene.render.image_settings.color_mode = 'RGBA'
563 fp = bpy.path.abspath('//textures' + sep + name)
564 try:
565 i.save_render(fp)
566 i.source = 'FILE'
567 if bpy.context.user_preferences.filepaths.use_relative_paths:
568 # can't always find the relative path (between drive letters on windows)
569 try:
570 i.filepath = bpy.path.relpath(fp)
571 except ValueError:
572 i.filepath = fp
573 else:
574 i.filepath = fp
575 i.name = name
576 except:
577 print("something wrong with", fp)
578 #THAT'S THE GENERATED FILES saved, pathed and reloaded
579 #now save other painted textures
580 bpy.ops.image.save_dirty()
584 def save_active_paint():
585 #for materials in current object
586 ob = bpy.context.object
587 for m in ob.material_slots:
588 for ts in m.material.texture_slots:
589 save_painted(ts)
590 return {'FINISHED'}
592 def save_all_paint():
593 #for all materials
594 for m in bpy.data.materials:
595 for ts in m.texture_slots:
596 save_painted(ts)
597 return {'FINISHED'}
600 class save_all_generated(bpy.types.Operator):
601 """Saves painted layers to disc"""
602 bl_idname = "paint.save_all_generated"
604 bl_label = "SAVE PAINT LAYERS"
607 @classmethod
608 def poll(cls, context):
609 return context.active_object != None
611 def execute(self, context):
612 return save_active_paint()
617 #-----------------------------------
618 class OBJECT_PT_SavePainted(bpy.types.Panel):
619 bl_label = "Save All Painted"
620 bl_space_type = "VIEW_3D"
621 bl_region_type = "UI"
622 #bl_context = "texturepaint"
624 @classmethod
625 def poll(cls, context):
626 return (context.image_paint_object)
628 def draw(self, context):
629 self.layout.operator('paint.save_all_generated')
631 def register():
632 bpy.utils.register_module(__name__)
634 def unregister():
635 bpy.utils.unregister_module(__name__)
637 if __name__ == "__main__":
638 register()