SVN maintenance.
[blender-addons-contrib.git] / space_view3d_paint_bprojection.py
1 bl_info = {
2     "name": "BProjection",
3     "description": "Help Clone tool",
4     "author": "kgeogeo",
5     "version": (1, 0),
6     "blender": (2, 6, 3),
7     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/bprojection",
8     "tracker_url":"http://projects.blender.org/tracker/index.php?func=detail&aid=30521&group_id=153&atid=468",
9     "category": "Paint"}
10
11 import bpy
12 from bpy.types import Panel, Operator
13 from bpy.props import IntProperty, FloatProperty, BoolProperty, IntVectorProperty, StringProperty, FloatVectorProperty, CollectionProperty
14 from bpy_extras import view3d_utils
15 import math
16 from math import *
17 import mathutils
18 from mathutils import *
19
20 # Main function for align the plan to view
21 def align_to_view(context):
22     ob = context.object        
23     rotation = ob.custom_rotation
24     scale = ob.custom_scale
25     z = ob.custom_location.z
26     pos = [ob.custom_location.x, ob.custom_location.y]
27     
28     reg = context.area.regions[4]        
29     width = reg.width
30     height = reg.height 
31     
32     sd = context.space_data    
33     r3d = sd.region_3d     
34     r3d.update()
35     vr = r3d.view_rotation
36     quat = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(float(rotation)))
37     v = Vector((pos[0],pos[1],z))
38     v.rotate(vr)
39
40     em = bpy.data.objects['Empty for BProjection']
41     img = bpy.data.textures['Texture for BProjection'].image
42     if img and img.size[1] != 0:
43         prop = img.size[0]/img.size[1]
44     else: prop = 1    
45     
46     if ob.custom_linkscale:    
47         em.scale = Vector((prop*scale[0], scale[0], 1))
48     else:
49         em.scale = Vector((prop*scale[0], scale[1], 1))
50     pos_cur = em.location - sd.cursor_location
51     rot_cur1 = em.rotation_euler.to_quaternion()
52     em.location = v + ob.location            
53     em.rotation_euler = Quaternion.to_euler(vr*quat)
54         
55     if ob.custom_c3d:
56         if ob.custom_old_scale != ob.custom_scale:
57             pos_cur = em.location - sd.cursor_location        
58         rot_cur2 = em.rotation_euler.to_quaternion()
59         rot_cur1.invert()
60         pos_cur.rotate(rot_cur1)
61         pos_cur.rotate(rot_cur2)
62         v = em.location - pos_cur
63         sd.cursor_location =  v
64
65 # Function to update the properties
66 def update_Location(self, context):          
67     align_to_view(context)
68
69 # Function to update the scaleUV
70 def update_UVScale(self, context):
71     v = Vector((context.object.custom_offsetuv[0]/10 + 0.5, context.object.custom_offsetuv[1]/10 + 0.5))
72     l = Vector((0.0,0.0))
73     ob = context.object
74     s = ob.custom_scaleuv
75     os = ob.custom_old_scaleuv 
76     scale = s - os
77     uvdata = ob.data.uv_layers.active.data
78     for i in range(trunc(pow(ob.custom_sub+1, 2)*4)):
79         vres =  v - uvdata[len(uvdata)-1-i].uv  
80         uvdata[len(uvdata)-1-i].uv.x = v.x - vres.x/os[0]*s[0]
81         uvdata[len(uvdata)-1-i].uv.y = v.y - vres.y/os[1]*s[1]
82
83     ob.custom_old_scaleuv = s  
84     align_to_view(context)
85
86 def update_PropUVScale(self, context):
87     ob = context.object
88     if ob.custom_linkscaleuv:
89         ob.custom_scaleuv = [ob.custom_propscaleuv,ob.custom_propscaleuv]
90
91 def update_LinkUVScale(self, context):
92     ob = context.object
93     if ob.custom_linkscaleuv:
94         ob.custom_propscaleuv = ob.custom_scaleuv.x
95         update_PropUVScale(self, context)
96     else:
97         update_UVScale(self, context) 
98         
99 # Function to update the offsetUV
100 def update_UVOffset(self, context):
101     ob = context.object
102     o = ob.custom_offsetuv
103     oo = ob.custom_old_offsetuv 
104     uvdata = ob.data.uv_layers.active.data
105     for i in range(trunc(pow(ob.custom_sub+1, 2)*4)):
106         uvdata[len(uvdata)-1-i].uv = [uvdata[len(uvdata)-1-i].uv[0] - oo[0]/10 + o[0]/10, uvdata[len(uvdata)-1-i].uv[1] - oo[1]/10 + o[1]/10]   
107     ob.custom_old_offsetuv = o
108     
109     align_to_view(context)
110
111 # Function to update the flip horizontal
112 def update_FlipUVX(self, context):          
113     uvdata = context.object.data.uv_layers.active.data
114     for i in range(trunc(pow(context.object.custom_sub+1, 2)*4)):
115         x = uvdata[len(uvdata)-1-i].uv[0]
116         uvdata[len(uvdata)-1-i].uv[0] = 1 - x
117     
118     align_to_view(context)
119
120 # Function to update the flip vertical
121 def update_FlipUVY(self, context):          
122     uvdata = context.object.data.uv_layers.active.data
123     for i in range(trunc(pow(context.object.custom_sub+1, 2)*4)):
124         y = uvdata[len(uvdata)-1-i].uv[1]
125         uvdata[len(uvdata)-1-i].uv[1] = 1 - y
126     
127     align_to_view(context)
128
129 # Function to update
130 def update_Rotation(self, context):              
131     if context.object.custom_rotc3d:
132         ob = context.object
133         angle = ob.custom_rotation - ob.custom_old_rotation
134         sd = context.space_data
135         vr = sd.region_3d.view_rotation.copy()        
136         c = sd.cursor_location.copy() - ob.location
137         e = bpy.data.objects['Empty for BProjection'].location - ob.location
138         vo = Vector((0.0, 0.0, 1.0))
139         vo.rotate(vr)
140         quat = mathutils.Quaternion(vo, math.radians(angle))
141         v = e-c
142         v.rotate(quat)
143         vr.invert()
144         v.rotate(vr)
145         c.rotate(vr)
146         context.object.custom_location = c + v
147     else:        
148         align_to_view(context)
149    
150     context.object.custom_old_rotation = context.object.custom_rotation
151
152 # Function to update scale
153 def update_Scale(self, context):              
154     ob = context.object
155     
156     if context.object.custom_scac3d:
157         #ob.custom_c3d = False
158         sd = context.space_data
159         r3d =  sd.region_3d
160         vr = r3d.view_rotation.copy()
161         vr.invert()
162         e = bpy.data.objects['Empty for BProjection'].location - ob.location
163         c = sd.cursor_location.copy() - ob.location
164         ce = e - c
165         
166         s = ob.custom_scale
167         os = ob.custom_old_scale
168         delta =  ob.custom_scale - ob.custom_old_scale
169         c.rotate(vr)
170         ce.rotate(vr)
171         
172         img = bpy.data.textures['Texture for BProjection'].image
173         if img and img.size[1] != 0:
174             prop = img.size[0]/img.size[1]
175         else: prop = 1
176         
177         v = Vector((s.x*ce.x/os.x, s.y*ce.y/os.y,0.0))
178         ob.custom_location = c + v
179         #ob.custom_c3d = True
180         
181
182     else:          
183         align_to_view(context)
184             
185     
186     ob.custom_old_scale = ob.custom_scale
187
188 def update_PropScale(self, context):
189     ob = context.object
190     if ob.custom_linkscale:
191         ob.custom_scale = [ob.custom_propscale,ob.custom_propscale]
192     
193 def update_LinkScale(self, context):
194     ob = context.object
195     if ob.custom_linkscale:
196         ob.custom_propscale = ob.custom_scale.x
197         update_PropScale(self, context)
198     else:
199         update_Scale(self, context) 
200
201 def update_activeviewname(self, context):
202     if self.custom_active:
203         context.object.custom_active_view = self.custom_active_view
204
205 class custom_props(bpy.types.PropertyGroup):
206     custom_location = FloatVectorProperty(name="Location", description="Location of the plan",
207                                            default=(0,0,-1.0),
208                                            subtype = 'XYZ', size=3)
209                                            
210     custom_rotation = FloatProperty(name="Rotation", description="Rotate the plane",
211                                      min=-180, max=180, default=0)
212                                          
213     custom_scale = FloatVectorProperty(name="Scales", description="Scale the planes",
214                                        subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2)
215     custom_propscale = FloatProperty(name="PropScale", description="Scale the Plan",
216                                            default=1.0,min = 0.1)
217                                                                                     
218     custom_linkscale = BoolProperty(name="linkscale", default=True)
219    
220     # UV properties
221     custom_scaleuv = FloatVectorProperty(name="ScaleUV", description="Scale the texture's UV",
222                                             default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2)
223     custom_propscaleuv = FloatProperty(name="PropScaleUV", description="Scale the texture's UV",
224                                            default=1.0,min = 0.01) 
225     custom_offsetuv = FloatVectorProperty(name="OffsetUV", description="Decal the texture's UV",
226                                             default=(0.0,0.0), subtype = 'XYZ', size=2)       
227     custom_linkscaleuv = BoolProperty(name="linkscaleUV", default=True)
228     custom_flipuvx = BoolProperty(name="flipuvx", default=False)
229     custom_flipuvy = BoolProperty(name="flipuvy", default=False)
230     
231     # other properties
232     custom_active= BoolProperty(name="custom_active", default=True)   
233     custom_expand = BoolProperty(name="expand", default=False)
234     
235     custom_active_view = StringProperty(name = "custom_active_view",default = "View",update = update_activeviewname)
236     
237     custom_image = StringProperty(name = "custom_image",default = "")
238     
239     custom_index = IntProperty()
240
241 # Function to create custom properties
242 def createcustomprops(context):
243     Ob = bpy.types.Object    
244     
245     # plane properties 
246     Ob.custom_location = FloatVectorProperty(name="Location", description="Location of the plan",
247                                            default=(5.0,0.0,-1.0),
248                                            subtype = 'XYZ', size=3, update = update_Location)
249                                            
250     Ob.custom_rotation = FloatProperty(name="Rotation", description="Rotate the plane",
251                                      min=-180, max=180, default=0,update = update_Rotation)
252                                      
253     Ob.custom_old_rotation = FloatProperty(name="old_Rotation", description="Old Rotate the plane",
254                                          min=-180, max=180, default=0)
255                                          
256     Ob.custom_scale = FloatVectorProperty(name="Scales", description="Scale the planes",
257                                           subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2,update = update_Scale)
258     Ob.custom_propscale = FloatProperty(name="PropScale", description="Scale the Plan",
259                                            default=1.0,min = 0.1,update = update_PropScale)
260     Ob.custom_old_scale = FloatVectorProperty(name="old_Scales", description="Old Scale the planes",
261                                           subtype = 'XYZ', default=(1.0, 1.0),min = 0.1, size=2)
262                                           
263     Ob.custom_linkscale = BoolProperty(name="linkscale", default=True, update = update_LinkScale)
264     
265                                 
266     Ob.custom_sub = IntProperty(name="Subdivide", description="Number of subdivision of the plan",
267                                      min=1, max=20, default=10)                                
268     
269     # UV properties
270     Ob.custom_scaleuv = FloatVectorProperty(name="ScaleUV", description="Scale the texture's UV",
271                                             default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2,update = update_UVScale)
272     Ob.custom_propscaleuv = FloatProperty(name="PropScaleUV", description="Scale the texture's UV",
273                                            default=1.0,min = 0.01,update = update_PropUVScale)    
274     Ob.custom_old_scaleuv = FloatVectorProperty(name="old_ScaleUV", description="Scale the texture's UV",
275                                                 default=(1.0,1.0),min = 0.01, subtype = 'XYZ', size=2)
276     Ob.custom_offsetuv = FloatVectorProperty(name="OffsetUV", description="Decal the texture's UV",
277                                             default=(0.0,0.0), subtype = 'XYZ', size=2,update = update_UVOffset)    
278     Ob.custom_old_offsetuv = FloatVectorProperty(name="old_OffsetUV", description="Decal the texture's UV",
279                                                  default=(0.0,0.0), subtype = 'XYZ', size=2)    
280     Ob.custom_linkscaleuv = BoolProperty(name="linkscaleUV", default=True, update = update_LinkUVScale)
281     Ob.custom_flipuvx = BoolProperty(name="flipuvx", default=False, update = update_FlipUVX)
282     Ob.custom_flipuvy = BoolProperty(name="flipuvy", default=False, update = update_FlipUVY)
283     
284     # other properties    
285     Ob.custom_c3d = BoolProperty(name="c3d", default=True)
286     Ob.custom_rot = BoolProperty(name="rot", default=True)
287     Ob.custom_rotc3d = BoolProperty(name="rotc3d", default=False)
288     Ob.custom_scac3d = BoolProperty(name="scac3d", default=False)
289     Ob.custom_expand = BoolProperty(name="expand", default=True)
290     Ob.custom_active_view = StringProperty(name = "custom_active_view",default = "View")
291     
292     Ob.custom_props = CollectionProperty(type = custom_props)
293
294 # Function to remove custom properties
295 def removecustomprops():    
296     list_prop = ['custom_location', 'custom_rotation', 'custom_old_rotation', 'custom_scale', 'custom_old_scale', 'custom_c3d',
297                  'custom_rot', 'custom_rotc3d', 'custom_scaleuv', 'custom_flipuvx', 'custom_flipuvy', 'custom_linkscale',
298                  'custom_linkscaleuv', 'custom_old_scaleuv', 'custom_offsetuv', 'custom_old_offsetuv', 'custom_scac3d', 'custom_sub',
299                  'custom_expand', 'custom_active_view', 'custom_propscaleuv', 'custom_props', 'custom_propscale']
300     for prop in list_prop:
301         try:
302             del bpy.context.object[prop]
303         except:
304             do = 'nothing'
305
306 # Oprerator Class to create view            
307 class CreateView(Operator):
308     bl_idname = "object.create_view"
309     bl_label = "Create a new view"
310
311     def execute(self, context):              
312         ob = context.object
313         new_props = ob.custom_props.add()
314         
315         ob.custom_active_view = new_props.custom_active_view               
316         new_props.custom_index = len(bpy.context.object.custom_props)-1
317         bpy.ops.object.active_view(index = new_props.custom_index)
318         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = True
319         bpy.ops.object.shape_key_add(from_mix = False)
320         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].value = 1.0
321         return {'FINISHED'}
322
323 # Oprerator Class to copy view 
324 class SaveView(Operator):
325     bl_idname = "object.save_view"
326     bl_label = "copy the view"
327     
328     index = IntProperty(default = 0)
329     
330     def execute(self, context):              
331         ob = context.object
332         prop = ob.custom_props[self.index]        
333         prop.custom_location =  ob.custom_location                    
334         prop.custom_rotation =  ob.custom_rotation                    
335         prop.custom_scale =  ob.custom_scale                  
336         prop.custom_linkscale =  ob.custom_linkscale                                      
337         prop.custom_scaleuv = ob.custom_scaleuv
338         prop.custom_propscale = ob.custom_propscale
339         prop.custom_offsetuv =  ob.custom_offsetuv  
340         prop.custom_linkscaleuv = ob.custom_linkscaleuv
341         prop.custom_propscaleuv = ob.custom_propscaleuv
342         prop.custom_flipuvx = ob.custom_flipuvx
343         prop.custom_flipuvy = ob.custom_flipuvy
344         try:
345             prop.custom_image = bpy.data.textures['Texture for BProjection'].image.name
346         except:
347             do = 'nothing'
348         
349         return {'FINISHED'}
350
351 # Oprerator Class to copy view 
352 class PasteView(Operator):
353     bl_idname = "object.paste_view"
354     bl_label = "paste the view"
355     
356     index = IntProperty(default = 0)
357     
358     def execute(self, context):              
359         ob = context.object
360         prop = ob.custom_props[self.index]
361         ob.custom_linkscale =  prop.custom_linkscale
362         ob.custom_offsetuv =  prop.custom_offsetuv 
363         ob.custom_linkscaleuv = prop.custom_linkscaleuv
364         ob.custom_scaleuv = prop.custom_scaleuv
365         ob.custom_propscaleuv = prop.custom_propscaleuv       
366         ob.custom_rotation =  prop.custom_rotation                    
367         ob.custom_scale =  prop.custom_scale
368         ob.custom_propscale = prop.custom_propscale 
369         ob.custom_location =  prop.custom_location                    
370         if prop.custom_image != '':
371             if bpy.data.textures['Texture for BProjection'].image.name != prop.custom_image:
372                 bpy.data.textures['Texture for BProjection'].image = bpy.data.images[prop.custom_image]
373                 bpy.ops.object.applyimage()
374         if ob.custom_flipuvx != prop.custom_flipuvx:
375             ob.custom_flipuvx = prop.custom_flipuvx
376         if ob.custom_flipuvy != prop.custom_flipuvy:
377             ob.custom_flipuvy = prop.custom_flipuvy
378         
379         return {'FINISHED'}
380
381 # Oprerator Class to remove view 
382 class RemoveView(Operator):
383     bl_idname = "object.remove_view"
384     bl_label = "Rmeove the view"
385     
386     index = IntProperty(default = 0)
387     
388     def execute(self, context):              
389         ob = context.object
390         
391         ob.active_shape_key_index =  self.index + 1
392         bpy.ops.object.shape_key_remove()
393         
394         if  context.object.custom_props[self.index].custom_active: 
395             if len(ob.custom_props) > 0:
396                 bpy.ops.object.active_view(index = self.index-1)
397             if self.index == 0 and len(ob.custom_props) > 1:
398                 bpy.ops.object.active_view(index = 1)            
399                 
400         ob.custom_props.remove(self.index)
401         
402         print(len(context.object.custom_props))
403                 
404         if len(context.object.custom_props) == 0:
405             ob.custom_scale = [1,1]
406             ob.custom_rotation = 0
407             ob.custom_scaleuv =[1.0,1.0]
408             ob.custom_offsetuv =[0.0,0.0]
409             ob.custom_propscaleuv = 1.0
410             ob.custom_propscale = 1.0
411             if ob.custom_flipuvx == True:
412                 ob.custom_flipuvx = False
413             if ob.custom_flipuvy == True:
414                 ob.custom_flipuvy = False
415             
416             bpy.ops.object.create_view()            
417                  
418         i=0
419         for item in context.object.custom_props:
420             item.custom_index = i           
421             i+=1 
422
423         for item in [item for item in context.object.custom_props if item.custom_active]:
424                 ob.active_shape_key_index = item.custom_index+1
425            
426         return {'FINISHED'}
427
428 # Oprerator Class to copy view 
429 class ActiveView(Operator):
430     bl_idname = "object.active_view"
431     bl_label = "Active the view"
432     
433     index = IntProperty(default = 0)
434     
435     def execute(self, context):
436         ob = context.object
437         for item in [ item for item in ob.custom_props if item.custom_active == True]:
438                 bpy.ops.object.save_view(index = item.custom_index)
439                 item.custom_active = False
440         ob.custom_props[self.index].custom_active  = True
441         ob.custom_active_view = ob.custom_props[self.index].custom_active_view 
442         ob.active_shape_key_index =  self.index + 1
443         
444         for i in ob.data.shape_keys.key_blocks:
445             i.mute = True
446         
447         ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = False
448         
449         bpy.ops.object.paste_view(index = self.index)         
450         
451         return {'FINISHED'}
452
453 # Draw Class to show the panel
454 class BProjection(Panel):
455     bl_space_type = 'VIEW_3D'
456     bl_region_type = 'UI'
457     bl_label = "BProjection"
458
459     @classmethod
460     def poll(cls, context):
461         return (context.image_paint_object or context.sculpt_object)
462
463     def draw(self, context):        
464         layout = self.layout
465                 
466         try: 
467             bpy.data.objects['Empty for BProjection']
468             
469             tex = bpy.data.textures['Texture for BProjection']
470             ob = context.object
471                         
472             col = layout.column(align =True)
473             col.operator("object.removebprojectionplane", text="Remove BProjection plane")           
474                 
475             box = layout.box()
476
477             row = box.row()
478             if not ob.custom_expand:
479                 row.prop(ob, "custom_expand", text  = "", icon="TRIA_RIGHT", emboss=False)
480                 row.label(text=ob.custom_active_view)
481             else:
482                 row.prop(ob, "custom_expand", text = "" , icon="TRIA_DOWN", emboss=False)                
483                 row.label(text=ob.custom_active_view)
484                 
485                 col = box.column(align =True)
486                 col.template_ID(tex, "image", open="image.open")
487                 row  = box.row(align=True)
488                 row.operator('object.applyimage', text="Apply image", icon = 'FILE_TICK')
489                 row.prop(ob, "custom_c3d",text="", icon='CURSOR')
490                 row.prop(ob, "custom_rot",text="", icon='ROTATE')
491                 row  = box.row(align =True)
492                 row.label(text="Location:")
493                 row  = box.row(align =True)
494                 row.prop(ob,'custom_location', text='')
495                 row  = box.row(align =True)            
496                 row.prop(ob,'custom_rotation')
497                 row.prop(ob,'custom_rotc3d',text="",icon='MANIPUL')            
498                 row  = box.row(align =True)
499                 row.label(text="Scale:")
500                 row  = box.row(align =True) 
501                 if ob.custom_linkscale :
502                     row.prop(ob, "custom_propscale",text="")
503                     row.prop(ob, "custom_linkscale",text="",icon='LINKED')
504                 else: 
505                     row.prop(ob,'custom_scale',text='')
506                     row.prop(ob, "custom_linkscale",text="",icon='UNLINKED')
507                 row.prop(ob,'custom_scac3d',text="",icon='MANIPUL')                            
508                 row  = box.row(align =True)
509                 row.label(text="UV's Offset:")
510                 row  = box.row(align =True)
511                 row.prop(ob,'custom_offsetuv',text='')
512                 row.prop(ob, "custom_flipuvx",text="",icon='ARROW_LEFTRIGHT')   
513                 row.prop(ob, "custom_flipuvy",text="",icon='FULLSCREEN_ENTER') 
514                 row  = box.row(align =True)
515                 row.label(text="UV's Scale:")
516                 row  = box.row(align =True)                            
517                 if ob.custom_linkscaleuv:
518                     row.prop(ob,'custom_propscaleuv',text='')
519                     row.prop(ob, "custom_linkscaleuv",text="",icon='LINKED')
520                 else: 
521                     row.prop(ob,'custom_scaleuv',text='')
522                     row.prop(ob, "custom_linkscaleuv",text="",icon='UNLINKED')            
523                 row = box.column(align =True)
524                 row.prop(ob.material_slots['Material for BProjection'].material,'alpha', slider = True)
525                 row = box.column(align =True)
526
527                 
528             for item in ob.custom_props:
529                 box = layout.box()
530                 row = box.row()
531                 if item.custom_active:
532                     row.operator("object.active_view",text = "", icon='RADIOBUT_ON', emboss = False).index = item.custom_index 
533                 else:
534                     row.operator("object.active_view",text = "", icon='RADIOBUT_OFF', emboss = False).index = item.custom_index 
535                 row.prop(item, "custom_active_view", text="")        
536                 row.operator('object.remove_view', text="", icon = 'PANEL_CLOSE', emboss = False).index = item.custom_index
537             row = layout.row()
538             row.operator('object.create_view', text="Create View", icon = 'RENDER_STILL')        
539
540         except:
541             col = layout.column(align = True)
542             col.operator("object.addbprojectionplane", text="Add BProjection plan")
543                    
544
545 # Oprerator Class to apply the image to the plane             
546 class ApplyImage(Operator):
547     bl_idname = "object.applyimage"
548     bl_label = "Apply image"
549
550     def execute(self, context):        
551         img = bpy.data.textures['Texture for BProjection'].image
552         em = bpy.data.objects['Empty for BProjection']
553         ob = context.object
554         cm = context.object.mode
555                
556         bpy.ops.object.editmode_toggle()
557         f = ob.data.polygons
558         nbface = len(ob.data.polygons)
559         uvdata = ob.data.uv_textures.active.data 
560         wasnul = False
561         if len(uvdata) == 0:
562             bpy.ops.object.editmode_toggle()
563             uvdata = ob.data.uv_textures.active.data
564             wasnul = True                        
565         
566         
567         vglen = trunc(pow(ob.custom_sub+1, 2))
568         
569         for i in range(vglen):  
570             uvdata[f[nbface-i-1].index].image = img
571         
572         if wasnul == False:
573             bpy.ops.object.editmode_toggle()
574         else:
575             bpy.ops.object.mode_set(mode = cm, toggle=False)
576                 
577         align_to_view(context)
578         
579         return {'FINISHED'}
580
581 # Oprerator Class to make the 4 or 6 point and scale the plan
582 class IntuitiveScale(Operator):
583     bl_idname = "object.intuitivescale"
584     bl_label = "Draw lines"
585
586     def invoke(self, context, event):
587         ob = context.object 
588         x = event.mouse_region_x
589         y = event.mouse_region_y                
590         if len(ob.grease_pencil.layers.active.frames) == 0: 
591             bpy.ops.gpencil.draw(mode='DRAW', stroke=[{"name":"", "pen_flip":False,
592                                                        "is_start":True, "location":(0, 0, 0),
593                                                        "mouse":(x,y), "pressure":1, "time":0}])
594         else:
595             if ob.custom_linkscale:
596                 nb_point = 4
597             else:
598                 nb_point = 6
599                    
600             if len(ob.grease_pencil.layers.active.frames[0].strokes) < nb_point:
601                 bpy.ops.gpencil.draw(mode='DRAW', stroke=[{"name":"", "pen_flip":False,
602                                                            "is_start":True, "location":(0, 0, 0),
603                                                            "mouse":(x,y), "pressure":1, "time":0}])
604                                                            
605             if len(ob.grease_pencil.layers.active.frames[0].strokes) == nb_point:
606                 s = ob.grease_pencil.layers.active.frames[0]
607                 v1 = s.strokes[1].points[0].co - s.strokes[0].points[0].co
608                 if not ob.custom_linkscale:
609                     v2 = s.strokes[4].points[0].co - s.strokes[3].points[0].co
610                 else:
611                     v2 = s.strokes[3].points[0].co - s.strokes[2].points[0].co
612                 propx = v1.x/v2.x                
613                 ob.custom_scale[0] *= abs(propx)
614                 
615                 if not ob.custom_linkscale:
616                     v1 = s.strokes[2].points[0].co - s.strokes[0].points[0].co
617                     v2 = s.strokes[5].points[0].co - s.strokes[3].points[0].co
618                     propy = v1.y/v2.y
619                     ob.custom_scale[1] *= abs(propy)
620                 bpy.ops.gpencil.active_frame_delete()
621         
622         return {'FINISHED'}
623
624 # Oprerator Class to configure all wath is needed
625 class AddBProjectionPlane(Operator):
626     bl_idname = "object.addbprojectionplane"
627     bl_label = "Configure"
628     
629     def creatematerial(self, context):        
630         try:
631             matBProjection = bpy.data.materials['Material for BProjection']
632         except:            
633             bpy.data.textures.new(name='Texture for BProjection',type='IMAGE')
634     
635             bpy.data.materials.new(name='Material for BProjection')
636             
637             matBProjection = bpy.data.materials['Material for BProjection']
638             matBProjection.texture_slots.add()
639             matBProjection.use_shadeless = True
640             matBProjection.use_transparency = True
641             matBProjection.active_texture = bpy.data.textures['Texture for BProjection']
642         
643             index = matBProjection.active_texture_index
644             matBProjection.texture_slots[index].texture_coords = 'UV'
645         
646         ob = context.object 
647         old_index = ob.active_material_index
648         bpy.ops.object.material_slot_add()
649         index = ob.active_material_index
650         ob.material_slots[index].material = bpy.data.materials['Material for BProjection']
651         bpy.ops.object.material_slot_assign()
652         ob.active_material_index = old_index
653         ob.data.update()
654             
655     def execute(self, context):    
656         try:
657             bpy.data.objects['Empty for BProjection']
658
659         except:            
660             createcustomprops(context)
661             cm = bpy.context.object.mode
662             bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False)
663             
664             context.space_data.show_relationship_lines = False
665             
666             ob = context.object
667         
668             bpy.ops.object.add()
669             em = context.object
670             em.name = "Empty for BProjection"
671                         
672             bpy.data.scenes['Scene'].objects.active = ob
673             ob.select = True
674     
675             bpy.ops.object.editmode_toggle()
676     
677             bpy.ops.mesh.primitive_plane_add()
678             bpy.ops.object.vertex_group_assign(new = True)
679             ob.vertex_groups.active.name = 'texture plane'   
680             bpy.ops.uv.unwrap()
681             
682             bpy.ops.object.editmode_toggle()
683             for i in range(4):
684                 ob.data.edges[len(ob.data.edges)-1-i].crease = 1
685             bpy.ops.object.editmode_toggle()
686
687             bpy.ops.mesh.subdivide(number_cuts = ob.custom_sub)
688     
689             em.select = True
690             bpy.ops.object.hook_add_selob()
691             em.select = False
692             em.hide = True   
693                      
694             self.creatematerial(context)
695   
696           
697             bpy.ops.gpencil.data_add()
698             ob.grease_pencil.draw_mode = 'VIEW'
699             bpy.ops.gpencil.layer_add()
700             ob.grease_pencil.layers.active.color = [1.0,0,0]
701             
702             bpy.ops.object.editmode_toggle()
703             
704             bpy.ops.object.shape_key_add(from_mix = False)
705             
706             bpy.ops.object.create_view()
707             # ----------------------------------------------
708             # XXX, this isnt future proof, DON'T USE INDEX's - campbell                    
709             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
710             km.keymap_items[3-1].idname = 'view3d.rotate_view3d'
711             km.keymap_items[19-1].idname = 'view3d.zoom_view3d'
712             km.keymap_items[19-1].properties.delta = 1.0
713             km.keymap_items[20-1].idname = 'view3d.zoom_view3d'
714             km.keymap_items[20-1].properties.delta = -1.0
715             km.keymap_items[4-1].idname = 'view3d.pan_view3d'
716             km.keymap_items[26-1].idname = 'view3d.preset_view3d'
717             km.keymap_items[26-1].properties.view = 'FRONT'
718             km.keymap_items[28-1].idname = 'view3d.preset_view3d'
719             km.keymap_items[28-1].properties.view = 'RIGHT'            
720             km.keymap_items[32-1].idname = 'view3d.preset_view3d'
721             km.keymap_items[32-1].properties.view = 'TOP'
722             km.keymap_items[34-1].idname = 'view3d.preset_view3d'
723             km.keymap_items[34-1].properties.view = 'BACK'
724             km.keymap_items[35-1].idname = 'view3d.preset_view3d'
725             km.keymap_items[35-1].properties.view = 'LEFT'            
726             km.keymap_items[36-1].idname = 'view3d.preset_view3d'
727             km.keymap_items[36-1].properties.view = 'BOTTOM'                                   
728             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
729             kmi = km.keymap_items.new("object.intuitivescale", 'LEFTMOUSE', 'PRESS', shift=True)
730                         
731             align_to_view(context)
732             
733             context.space_data.cursor_location = em.location
734             
735             bpy.ops.object.mode_set(mode = cm, toggle=False)
736             
737         return {'FINISHED'}
738
739 # Oprerator Class to remove what is no more needed    
740 class RemoveBProjectionPlane(Operator):
741     bl_idname = "object.removebprojectionplane"
742     bl_label = "Configure"
743
744     def removematerial(self, context):
745         ob = context.object 
746         i = 0
747
748         for ms in ob.material_slots:
749             if ms.name == 'Material for BProjection':
750                 index = i
751             i+=1
752                 
753         ob.active_material_index = index
754         bpy.ops.object.material_slot_remove()
755     
756     def execute(self, context):
757         try:               
758             cm = bpy.context.object.mode
759             bpy.ops.object.mode_set(mode = 'OBJECT', toggle=False)
760             
761             context.space_data.show_relationship_lines = True
762             
763             bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
764             
765             self.removematerial(context)
766
767             ob = context.object
768     
769             bpy.ops.object.editmode_toggle()
770     
771             bpy.ops.mesh.reveal()
772                                    
773             bpy.ops.mesh.select_all()
774             bpy.ops.object.editmode_toggle() 
775             if ob.data.vertices[0].select:
776                 bpy.ops.object.editmode_toggle()
777                 bpy.ops.mesh.select_all()
778                 bpy.ops.object.editmode_toggle()
779             bpy.ops.object.editmode_toggle()                    
780             
781             ob.vertex_groups.active.name = 'texture plane'
782             bpy.ops.object.vertex_group_select()
783             bpy.ops.mesh.delete()
784             bpy.ops.object.vertex_group_remove()
785     
786             bpy.ops.object.editmode_toggle()
787    
788             ob.select = False
789                 
790             em = bpy.data.objects['Empty for BProjection']
791             bpy.data.scenes['Scene'].objects.active = em
792             em.hide = False
793             em.select = True
794             bpy.ops.object.delete()
795     
796             bpy.data.scenes['Scene'].objects.active = ob
797             ob.select = True
798             
799             km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
800             # ----------------------------------------------
801             # XXX, this isnt future proof, DON'T USE INDEX's - campbell
802             km.keymap_items[3-1].idname = 'view3d.rotate'
803             km.keymap_items[19-1].idname = 'view3d.zoom'
804             km.keymap_items[19-1].properties.delta = 1.0
805             km.keymap_items[20-1].idname = 'view3d.zoom'
806             km.keymap_items[20-1].properties.delta = -1.0
807             km.keymap_items[4-1].idname = 'view3d.move'
808             km.keymap_items[26-1].idname = 'view3d.viewnumpad'
809             km.keymap_items[26-1].properties.type = 'FRONT'
810             km.keymap_items[28-1].idname = 'view3d.viewnumpad'
811             km.keymap_items[28-1].properties.type = 'RIGHT'            
812             km.keymap_items[32-1].idname = 'view3d.viewnumpad'
813             km.keymap_items[32-1].properties.type = 'TOP'
814             km.keymap_items[34-1].idname = 'view3d.viewnumpad'
815             km.keymap_items[34-1].properties.type = 'BACK'
816             km.keymap_items[35-1].idname = 'view3d.viewnumpad'
817             km.keymap_items[35-1].properties.type = 'LEFT'            
818             km.keymap_items[36-1].idname = 'view3d.viewnumpad'
819             km.keymap_items[36-1].properties.type = 'BOTTOM'            
820             
821             km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
822             #to do
823             for kmi in [kmi for kmi in km.keymap_items if kmi.idname in {"object.intuitivescale", }]:
824                     km.keymap_items.remove(kmi)
825             
826             bpy.ops.object.mode_set(mode = cm, toggle=False)
827             
828             for i in ob.data.shape_keys.key_blocks:
829                 bpy.ops.object.shape_key_remove()
830             bpy.ops.object.shape_key_remove()    
831             
832             removecustomprops()
833                     
834         except:
835             nothing = 0
836         
837         return {'FINISHED'}
838
839 # Oprerator Class to rotate the view3D
840 class RotateView3D(Operator):
841     bl_idname = "view3d.rotate_view3d"
842     bl_label = "Rotate the View3D"
843     
844     first_mouse = Vector((0,0))
845
846     pan = Vector((0,0))
847
848     key = ['']
849  
850     first_time = True
851     
852     def vect_sphere(self, context, mx, my):
853         width = context.area.regions[4].width
854         height = context.area.regions[4].height
855            
856         if width >= height:
857             ratio = height/width
858         
859             x = 2*mx/width
860             y = 2*ratio*my/height
861             
862             x = x - 1
863             y = y - ratio
864         else:
865             ratio = width/height
866         
867             x = 2*ratio*mx/width
868             y = 2*my/height
869  
870             x = x - ratio
871             y = y - 1
872         
873         z2 = 1 - x * x - y * y
874         if z2 > 0:
875             z= sqrt(z2)
876         else : z=0
877             
878         p = Vector((x, y, z))
879         p.normalize()
880         return p
881     
882     def tracball(self, context, mx, my, origine):
883         sd = context.space_data
884         ob = context.object         
885         if ob.custom_rot:
886             vr_b = sd.region_3d.view_rotation.copy()
887             vr_b.invert()
888             pos_init = sd.region_3d.view_location - origine
889             sd.region_3d.view_location = origine
890         
891         v1 = self.vect_sphere(context, self.first_mouse.x, self.first_mouse.y)
892         v2 = self.vect_sphere(context, mx, my)
893                         
894         axis = Vector.cross(v1,v2);
895         angle = Vector.angle(v1,v2);
896             
897         q =  Quaternion(axis,-2*angle)
898                         
899         sd.region_3d.view_rotation *=q
900         sd.region_3d.update()
901         
902         if ob.custom_rot:
903             vr_a = sd.region_3d.view_rotation.copy()                           
904             pos_init.rotate(vr_a*vr_b)            
905             sd.region_3d.view_location =  pos_init + origine
906         
907         self.first_mouse = Vector((mx, my))
908                 
909     def modal(self, context, event):                                
910         ob = context.object 
911         reg = context.area.regions[4]        
912         if event.value == 'PRESS':
913             self.pan = Vector((event.mouse_region_x, event.mouse_region_y))
914                     
915             self.key = [event.type]
916
917         if event.value == 'RELEASE':
918             self.key = [''] 
919
920         if event.type == 'MOUSEMOVE':                        
921             
922             if '' in self.key:
923                 self.tracball(context, event.mouse_region_x, event.mouse_region_y,ob.location)
924                 align_to_view(context)
925                 if self.first_time:
926                     rot_ang = context.user_preferences.view.rotation_angle            
927                     context.user_preferences.view.rotation_angle = 0
928                     bpy.ops.view3d.view_orbit(type='ORBITLEFT')
929                     context.user_preferences.view.rotation_angle = rot_ang   
930                     bpy.ops.view3d.view_persportho()         
931                     bpy.ops.view3d.view_persportho()
932                     self.first_time = False
933           
934             deltax = event.mouse_region_x - round(self.pan.x)
935             deltay = event.mouse_region_y - round(self.pan.y)          
936
937             if 'G' in self.key:       
938                 sd = context.space_data              
939                 l =  sd.region_3d
940                 vr = l.view_rotation.copy()
941                 vr.invert()
942                 
943                 v_init = Vector((0.0,0.0,1.0))
944                 
945                 pos = [-deltax,-deltay]
946                 v = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
947                 pos = [0,0]
948                 vbl = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)        
949                 loc = vbl - v            
950                 loc.rotate(vr)
951                 ob.custom_location += loc              
952                                    
953             if 'S' in self.key:                
954                 s = ob.custom_scale
955                 if ob.custom_linkscale:
956                     ob.custom_propscale += deltax/20
957                 else:
958                     ob.custom_scale = [s[0] + deltax/20, s[1] + deltay/20]
959                                           
960             if 'Z' in self.key:                
961                 ob.custom_location.z+=deltax/10
962                       
963             if 'R' in self.key:
964                 ob.custom_rotation+=deltax
965                     
966             if 'U' in self.key:
967                 suv = ob.custom_scaleuv
968                 if ob.custom_linkscaleuv:    
969                     ob.custom_propscaleuv += deltax/50
970                 else:
971                     ob.custom_scaleuv= [suv[0] + deltax/50 , suv[1] + deltay/50]               
972
973             if 'Y' in self.key:       
974                 ouv = ob.custom_offsetuv
975                 ob.custom_offsetuv = [ouv[0] - deltax/50,ouv[1] - deltay/50] 
976
977             self.pan = Vector((event.mouse_region_x, event.mouse_region_y))
978             self.first_mouse = Vector((event.mouse_region_x, self.first_mouse.y))
979                         
980         elif event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':       
981             
982             return {'FINISHED'}
983         
984         if 'C' in self.key:
985             ob.custom_scale = [1,1]
986             ob.custom_rotation = 0
987             ob.custom_scaleuv =[1.0,1.0]
988             ob.custom_offsetuv =[0.0,0.0]
989             ob.custom_propscaleuv = 1.0
990             ob.custom_propscale = 1.0
991             if ob.custom_flipuvx == True:
992                 ob.custom_flipuvx = False
993             if ob.custom_flipuvy == True:
994                 ob.custom_flipuvy = False
995                     
996         return {'RUNNING_MODAL'}
997     
998     def execute(self, context):        
999         align_to_view(context)  
1000         
1001         return{'FINISHED'}
1002
1003     def invoke(self, context, event):
1004         context.window_manager.modal_handler_add(self)
1005         self.first_mouse = Vector((event.mouse_region_x,event.mouse_region_y))
1006         self.first_time = True
1007         
1008         return {'RUNNING_MODAL'}
1009
1010 # Oprerator Class to pan the view3D
1011 class PanView3D(bpy.types.Operator):
1012     bl_idname = "view3d.pan_view3d"
1013     bl_label = "Pan View3D"
1014     
1015     first_mouse = Vector((0,0))
1016
1017     def modal(self, context, event):
1018         ob = context.object
1019         width = context.area.regions[4].width
1020         height = context.area.regions[4].height
1021
1022         deltax = event.mouse_region_x - self.first_mouse.x
1023         deltay = event.mouse_region_y - self.first_mouse.y                
1024         
1025         sd = context.space_data              
1026         r3d =  sd.region_3d
1027         vr = r3d.view_rotation.copy()
1028         vr.invert()
1029         
1030         v_init = Vector((0.0,0.0,1.0))
1031         
1032         pos = [deltax,deltay]
1033         v = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1034         pos = [0,0]
1035         vbl = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)        
1036         loc = vbl - v       
1037         sd.region_3d.view_location += loc         
1038         loc.rotate(vr)
1039         ob.custom_location += loc
1040
1041         self.first_mouse.x = event.mouse_region_x
1042         self.first_mouse.y = event.mouse_region_y
1043
1044         if event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':
1045             return {'FINISHED'}
1046         
1047         return {'RUNNING_MODAL'}
1048                 
1049     def invoke(self, context, event):
1050         context.window_manager.modal_handler_add(self)
1051         self.first_mouse.x = event.mouse_region_x
1052         self.first_mouse.y = event.mouse_region_y   
1053         
1054         return {'RUNNING_MODAL'}
1055
1056     def execute(self, context):        
1057         align_to_view(context)  
1058         
1059         return{'FINISHED'}
1060
1061 # Oprerator Class to zoom the view3D
1062 class ZoomView3D(Operator):
1063     bl_idname = "view3d.zoom_view3d"
1064     bl_label = "Zoom View3D"
1065
1066     delta = FloatProperty(
1067         name="delta",
1068         description="Delta",
1069         min=-1.0, max=1,
1070         default=1.0)
1071
1072     def invoke(self, context, event):                   
1073         ob = context.object 
1074         sd = context.space_data
1075
1076         width = context.area.regions[4].width
1077         height = context.area.regions[4].height              
1078                     
1079         r3d =  sd.region_3d
1080         v_init = Vector((0.0,0.0,1.0))
1081
1082         pos = [width,height]
1083         vtr_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1084         pos = [0,0]
1085         vbl_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1086         len_b = vtr_b - vbl_b
1087        
1088         bpy.ops.view3d.zoom(delta = self.delta)
1089         r3d.update()
1090
1091         pos = [width,height]
1092         vtr_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
1093         pos = [0,0]
1094         vbl_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init) 
1095         len_a = vtr_a - vbl_a
1096         
1097         fac = len_a.length/len_b.length
1098         r3d.view_location -= ob.location
1099         r3d.view_location *= fac
1100         r3d.view_location += ob.location
1101         vres = Vector((ob.custom_location.x*fac,ob.custom_location.y*fac,ob.custom_location.z))
1102         ob.custom_location = vres
1103         
1104         
1105         align_to_view(context)
1106         
1107         return {'FINISHED'}
1108
1109     def execute(self, context):        
1110         align_to_view(context)
1111         
1112         return{'FINISHED'}
1113
1114 # Oprerator Class to use numpad shortcut
1115 class PresetView3D(Operator):
1116     bl_idname = "view3d.preset_view3d"
1117     bl_label = "Preset View3D"
1118
1119     view = StringProperty(name="View", description="Select the view", default='TOP')
1120
1121     def invoke(self, context, event):                   
1122         ob = context.object 
1123         origine = ob.location
1124         sd = context.space_data
1125
1126         if ob.custom_rot:
1127             vr_b = sd.region_3d.view_rotation.copy()
1128             vr_b.invert()
1129             pos_init = sd.region_3d.view_location - origine
1130             sd.region_3d.view_location = origine
1131
1132         tmp = context.user_preferences.view.smooth_view
1133         context.user_preferences.view.smooth_view = 0
1134         bpy.ops.view3d.viewnumpad(type=self.view)
1135         align_to_view(context)        
1136         context.user_preferences.view.smooth_view = tmp
1137      
1138         if ob.custom_rot:
1139             vr_a = sd.region_3d.view_rotation.copy()                           
1140             pos_init.rotate(vr_a*vr_b)            
1141             sd.region_3d.view_location =  pos_init + origine
1142                
1143                     
1144         return {'FINISHED'}
1145
1146 def register():
1147     bpy.utils.register_module(__name__)
1148
1149 def unregister():
1150     bpy.utils.unregister_module(__name__)
1151
1152 if __name__ == "__main__":
1153     register()