fix [#36995] FBX Importer does not import fbx model
[blender-addons.git] / texture_paint_layer_manager.py
blob0a4e23a5e256c052985e45a9a510f5f40becea23
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/Scripts/3D_interaction/Texture_paint_layers",
10 "tracker_url": "http://projects.blender.org/tracker/index.php?func=detail&aid=26789",
11 "category": "Paint"}
14 import bpy
15 from bpy.props import*
16 import os
17 from bpy_extras.io_utils import ImportHelper
20 #-------------------------------------------
22 def load_a_brush(context, filepath):
23 if os.path.isdir(filepath):
24 return
26 else:
28 try:
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
34 #create a texture
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
40 tex.image = img
42 except:
43 print(f,'is not image?')
45 return {'FINISHED'}
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"
56 @classmethod
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):
67 directory = filepath
69 else:
70 #is a file, find parent directory
71 li = filepath.split(os.sep)
72 directory = filepath.rstrip(li[-1])
75 files = os.listdir(directory)
76 for f in files:
77 try:
78 fn = f[3:]
79 #create image and load...
80 img = bpy.data.images.load(filepath = directory +os.sep + f)
81 img.use_fake_user =True
83 #create a texture
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
89 tex.image = img
91 except:
92 print(f,'is not image?')
93 continue
94 return {'FINISHED'}
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"
105 @classmethod
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"
120 @classmethod
121 def poll(cls, context):
122 return (context.sculpt_object or context.image_paint_object)
124 def draw(self, context):
125 layout = self.layout
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"
143 @classmethod
144 def poll(cls, context):
145 return (context.image_paint_object)
147 def draw(self, context):
148 layout = self.layout
150 ob = bpy.context.image_paint_object
151 if ob:
152 mat = ob.active_material
153 if not mat:
154 row = layout.row()
155 row.label(' Add a Material first!', icon = 'ERROR')
156 else:
157 row = layout.row()
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
163 i = -1
164 for t in mat.texture_slots:
165 i+=1
166 try:
167 if t.texture.type =='IMAGE':
168 row = layout.row(align= True)
169 if t.texture == mat.active_texture:
170 ai = 'BRUSH_DATA'
171 else:
172 ai = 'BLANK1'
173 row.operator('object.set_active_paint_layer',
174 text = "", icon = ai).tex_index =i
175 row.prop(t.texture,'name', text = "")
178 #Visibility
179 if t.use:
180 ic = 'RESTRICT_VIEW_OFF'
181 else:
182 ic = 'RESTRICT_VIEW_ON'
183 row.prop(t,'use', text = "",icon = ic)
184 except:
185 continue
192 ts = mat.texture_slots[mat.active_texture_index]
194 if ts:
195 row = layout.row()
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)
208 if ts.use_map_alpha:
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)
221 if ts.use_map_warp:
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)
228 if ts.use_map_emit:
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='')
238 else:
239 row=layout.row()
240 row.label('No paint layers in material', icon = 'ERROR')
243 # row = layout.row()
244 # row.label('')
245 # row = layout.row()
246 # row.label('WIP: Use the X to delete!:')
247 # row = layout.row()
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"
257 @classmethod
258 def poll(cls, context):
259 return (context.image_paint_object)
261 def draw(self, context):
262 layout = self.layout
264 ob = bpy.context.image_paint_object
265 if ob:
266 mat = ob.active_material
268 if mat:
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'
304 else:
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
312 me = ob.data
313 mat = ob.active_material
314 mat.active_texture_index = tn
315 ts = mat.texture_slots[tn]
317 #make sure it's visible
318 ts.use = True
320 #Mesh use UVs?
321 if not me.uv_textures:
322 bpy.ops.mesh.uv_texture_add()
324 # texture Slot uses UVs?
325 if ts.texture_coords == 'UV':
326 if ts.uv_layer:
327 uvtex = me.uv_textures[ts.uv_layer]
329 else:
330 uvtex = me.uv_textures.active
331 me.uv_textures.active= uvtex
332 else:
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
343 #get material index
344 m_id = ob.active_material_index
346 if img:
347 for f in me.polygons:
348 if f.material_index == m_id:
349 uvtex[f.index].image = img
353 else:
354 for f in me.polygons:
355 if f.material_index == m_id:
356 uvtex[f.index].image = None
358 me.update()
366 class set_active_paint_layer(bpy.types.Operator):
367 """"""
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)
373 @classmethod
374 def poll(cls, context):
375 return context.active_object != None
377 def execute(self, context):
378 tn = self.tex_index
379 main(context, tn)
380 return {'FINISHED'}
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
388 tl =[]
389 for i in bpy.data.images:
390 tl.append(i.name)
393 #create a new image
395 bpy.ops.image.new(name =iname,width =iwidth,height =iheight,
396 color = icolor, float = nfloat)
398 #find its creation index
399 it = 0
400 for i in bpy.data.images:
401 if i.name not in tl:
402 return(bpy.data.images[it])
403 break
404 it += 1
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()
412 ifloat = False
414 if typ =='NORMAL':
415 color =(0.5,0.5,0.5,1.0)
416 iname = 'Bump'
417 ifloat = True
418 elif typ =='COLOR':
419 iname ='Color'
420 color = (1.0,1.0,1.0,0.0)
422 elif typ =='ALPHA':
423 iname ='Alpha'
424 color = (1.0,1.0,1.0,0.0)
425 else:
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', '')
431 bn = ob.name
433 iname = bn +'_' + iname
435 tex = bpy.data.textures.new(name = iname, type = 'IMAGE')
436 ts.texture = tex
437 img = add_image_kludge(iname = typ,
438 iwidth = size,iheight = size, icolor= color, nfloat = ifloat)
439 tex.image = img
441 if typ == 'COLOR':
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'
458 elif typ == 'EMIT':
459 ts.use_map_emit = True
460 ts.use_map_color_diffuse =False
461 ts.use_rgb_to_intensity = True
463 elif typ == 'ALPHA':
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
507 i = 0
508 ts_index = None
509 for t in mat.texture_slots:
510 if t == ts:
512 ts_index = i
513 break
514 i += 1
515 if ts_index != None:
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):
526 """"""
527 bl_idname = "object.add_paint_layer"
528 bl_label = "Add Paint Layer"
529 ttype = StringProperty(name ='ttype',default ='NORMAL')
531 @classmethod
532 def poll(cls, context):
533 return context.active_object != None
535 def execute(self, context):
536 ttype = self.ttype
537 add_paint(context,typ= ttype)
538 return {'FINISHED'}
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
550 if ts:
551 if ts.texture.type =='IMAGE':
552 i = ts.texture.image
553 if i.source =='GENERATED':
554 if i.is_dirty:
555 name = ts.name
556 if i.file_format =='PNG':
557 name = name + '.png'
558 elif i.file_format =='TARGA':
559 name = name +'.tga'
561 bpy.context.scene.render.image_settings.color_mode = 'RGBA'
562 fp = bpy.path.abspath('//textures' + sep + name)
563 try:
564 i.save_render(fp)
565 i.source = 'FILE'
566 if bpy.context.user_preferences.filepaths.use_relative_paths:
567 # can't always find the relative path (between drive letters on windows)
568 try:
569 i.filepath = bpy.path.relpath(fp)
570 except ValueError:
571 i.filepath = fp
572 else:
573 i.filepath = fp
574 i.name = name
575 except:
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:
588 save_painted(ts)
589 return {'FINISHED'}
591 def save_all_paint():
592 #for all materials
593 for m in bpy.data.materials:
594 for ts in m.texture_slots:
595 save_painted(ts)
596 return {'FINISHED'}
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"
606 @classmethod
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"
623 @classmethod
624 def poll(cls, context):
625 return (context.image_paint_object)
627 def draw(self, context):
628 self.layout.operator('paint.save_all_generated')
630 def register():
631 bpy.utils.register_module(__name__)
633 def unregister():
634 bpy.utils.unregister_module(__name__)
636 if __name__ == "__main__":
637 register()