Added applying the easy lattice modifier while in lattice object. If the lattice...
[blender-addons-contrib.git] / mesh_easylattice.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19
20 bl_info = {
21             "name": "Easy Lattice Object",
22             "author": "Kursad Karatas",
23             "version": ( 0, 5 ),
24             "blender": ( 2, 66, 0 ),
25             "location": "View3D > Easy Lattice",
26             "description": "Create a lattice for shape editing",
27             "warning": "",
28             "wiki_url": "http://wiki.blender.org/index.php/Easy_Lattice_Editing_Addon",
29             "tracker_url": "https://bitbucket.org/kursad/blender_addons_easylattice/src",
30             "category": "Mesh"}
31
32 import bpy
33 import mathutils
34 import math
35  
36 # Cleanup
37 def modifiersDelete( obj ):
38     
39     for mod in obj.modifiers:
40         print(mod)
41         if mod.name == "latticeeasytemp":
42             try:
43                 if mod.object == bpy.data.objects['LatticeEasytTemp']:
44                     print("applying modifier")
45                     bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name )
46                     
47             except:
48                 bpy.ops.object.modifier_remove( modifier = mod.name )
49         
50 # Cleanup
51 def modifiersApplyRemove( obj ):
52     
53     print("passed object is", obj)
54     
55     print("current object is", bpy.context.active_object)
56     
57     bpy.ops.object.select_all( action = 'DESELECT' )
58     bpy.ops.object.select_pattern(pattern=obj.name, extend=False)
59     bpy.context.scene.objects.active=obj
60     
61     for mod in obj.modifiers:
62         print("modifier is ", mod)
63         if mod.name == "latticeeasytemp":
64 #             try:
65             if mod.object == bpy.data.objects['LatticeEasytTemp']:
66                 print("mod object is ", mod.object)
67                 print("applying modifier", mod," - ", mod.name)
68                 
69                 #obj.select= True
70                 print("current object is", bpy.context.active_object)
71                 bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name )
72                 #obj.modifiers.remove(mod)
73                     
74 #             except:
75 #                 bpy.ops.object.modifier_remove( modifier = mod.name )
76                 
77        
78 # Cleanup
79 def latticeDelete(obj):
80     bpy.ops.object.select_all( action = 'DESELECT' )
81     for ob in bpy.context.scene.objects:
82          if "LatticeEasytTemp" in ob.name:
83              ob.select = True
84     bpy.ops.object.delete( use_global = False )        
85     
86     #select the original object back
87     obj.select=True
88
89
90 def createLattice( obj, size, pos, props ):
91     # Create lattice and object
92     lat = bpy.data.lattices.new( 'LatticeEasytTemp' )
93     ob = bpy.data.objects.new( 'LatticeEasytTemp', lat )
94     
95     loc,rot,scl = getTransformations( obj )
96  
97     #get the combined rotation matrix and apply to the lattice
98     #ob.matrix_world=buildRot_WorldMat(obj)*ob.matrix_world
99     
100     #the position comes from the bbox 
101     ob.location = pos
102         #ob.location=(pos.x+loc.x,pos.y+loc.y,pos.z+loc.z)
103     
104     #the size  from bbox bbox
105     ob.scale = size
106         #ob.scale=(size.x*scl.x, size.y*scl.y,size.z*scl.z)
107     
108     #the rotation comes from the combined obj world matrix which was converted to euler pairs.    
109     ob.rotation_euler = buildRot_World(obj)
110     
111     ob.show_x_ray = True
112     # Link object to scene
113     scn = bpy.context.scene
114     scn.objects.link( ob )
115     scn.objects.active = ob
116     scn.update()
117  
118     # Set lattice attributes
119     lat.interpolation_type_u = props[3]
120     lat.interpolation_type_v = props[3]
121     lat.interpolation_type_w = props[3]
122  
123     lat.use_outside = False
124     
125     lat.points_u = props[0]
126     lat.points_v = props[1]
127     lat.points_w = props[2]
128
129     #Set lattice points
130     '''s = 0.0
131     points = [
132         (-s,-s,-s), (s,-s,-s), (-s,s,-s), (s,s,-s),
133         (-s,-s,s), (s,-s,s), (-s,s,s), (s,s,s)
134     ]
135     for n,pt in enumerate(lat.points):
136         for k in range(3):
137             #pt.co[k] = points[n][k]
138     '''
139     
140     return ob
141
142
143 def selectedVerts_Grp( obj ):
144 #     vertices=bpy.context.active_object.data.vertices
145     vertices = obj.data.vertices
146     
147     selverts = []
148     
149     if obj.mode == "EDIT":
150         bpy.ops.object.editmode_toggle()
151
152     for grp in obj.vertex_groups:
153         
154         if "templatticegrp" in grp.name:
155             bpy.ops.object.vertex_group_set_active( group = grp.name )
156             bpy.ops.object.vertex_group_remove()
157         
158     tempgroup = obj.vertex_groups.new( "templatticegrp" )
159     
160     # selverts=[vert for vert in vertices if vert.select==True]
161     for vert in vertices:
162         if vert.select == True:
163             selverts.append( vert )
164             tempgroup.add( [vert.index], 1.0, "REPLACE" )
165     
166     # print(selverts)
167     
168     return selverts
169
170 def getTransformations( obj ):
171     rot = obj.rotation_euler
172     loc = obj.location
173     size = obj.scale
174
175     return [loc, rot, size]
176
177 def findBBox( obj, selvertsarray ):
178     
179 #     mat = buildTrnSclMat( obj )
180     mat =buildTrnScl_WorldMat(obj)
181     
182     mat_world = obj.matrix_world
183     
184     minx, miny, minz = selvertsarray[0].co
185     maxx, maxy, maxz = selvertsarray[0].co
186     
187     c = 1
188 #     for vert in selvertsarray:
189     for c in range( len( selvertsarray ) ):
190         # co=obj.matrix_world*vert.co.to_4d()
191         
192 #         co = vert.co
193         co = selvertsarray[c].co
194         
195         if co.x < minx: minx = co.x
196         if co.y < miny: miny = co.y
197         if co.z < minz: minz = co.z
198
199         if co.x > maxx: maxx = co.x
200         if co.y > maxy: maxy = co.y
201         if co.z > maxz: maxz = co.z
202         
203 #         print("local cord", selvertsarray[c].co)
204 #         print("world cord", co)
205         c += 1
206         
207 #     print("total verts", len(selvertsarray))
208 #     print("counted verts",c)
209     
210     # Based on world coords
211 #     print("-> minx miny minz",minx, miny, minz )
212 #     print("-> maxx maxy maxz",maxx, maxy, maxz )
213     
214     minpoint = mathutils.Vector( ( minx, miny, minz ) )
215     maxpoint = mathutils.Vector( ( maxx, maxy, maxz ) )
216     
217     # middle point has to be calculated based on the real world matrix
218     #middle = mat_world * mathutils.Vector((x_sum, y_sum, z_sum))/float(c)
219     middle = ( ( minpoint + maxpoint ) / 2 )
220
221     minpoint = mat * minpoint  # Calculate only based on loc/scale
222     maxpoint = mat * maxpoint  # Calculate only based on loc/scale
223     middle = mat_world * middle  # the middle has to be calculated based on the real world matrix
224     
225     size = maxpoint - minpoint
226     size = mathutils.Vector( ( abs( size.x ), abs( size.y ), abs( size.z ) ) )
227     
228     # local coords   
229     #####################################################
230     '''minpoint=mathutils.Vector((minx,miny,minz))
231     maxpoint=mathutils.Vector((maxx,maxy,maxz))
232     middle=mathutils.Vector( (x_sum/float(len(selvertsarray)), y_sum/float(len(selvertsarray)), z_sum/float(len(selvertsarray))) )
233     size=maxpoint-minpoint
234     size=mathutils.Vector((abs(size.x),abs(size.y),abs(size.z)))
235     '''
236     #####################################################
237     
238
239     return [minpoint, maxpoint, size, middle  ]
240
241
242 def buildTrnSclMat( obj ):
243     # This function builds a local matrix that encodes translation and scale and it leaves out the rotation matrix
244     # The rotation is applied at obejct level if there is any
245     mat_trans = mathutils.Matrix.Translation( obj.location )
246     mat_scale = mathutils.Matrix.Scale( obj.scale[0], 4, ( 1, 0, 0 ) )
247     mat_scale *= mathutils.Matrix.Scale( obj.scale[1], 4, ( 0, 1, 0 ) )
248     mat_scale *= mathutils.Matrix.Scale( obj.scale[2], 4, ( 0, 0, 1 ) )
249     
250     mat_final = mat_trans * mat_scale
251     
252     
253     return mat_final
254     
255 def buildTrnScl_WorldMat( obj ):
256     # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix
257     # The rotation is applied at obejct level if there is any
258     loc,rot,scl=obj.matrix_world.decompose()
259     mat_trans = mathutils.Matrix.Translation( loc)
260     
261     mat_scale = mathutils.Matrix.Scale( scl[0], 4, ( 1, 0, 0 ) )
262     mat_scale *= mathutils.Matrix.Scale( scl[1], 4, ( 0, 1, 0 ) )
263     mat_scale *= mathutils.Matrix.Scale( scl[2], 4, ( 0, 0, 1 ) )
264     
265     mat_final = mat_trans * mat_scale
266     
267     
268     return mat_final
269
270 #Feature use    
271 def buildRot_WorldMat( obj ):
272     # This function builds a real world matrix that encodes rotation and it leaves out translation and scale matrices
273     loc,rot,scl=obj.matrix_world.decompose()
274     rot=rot.to_euler()
275     
276     mat_rot = mathutils.Matrix.Rotation(rot[0], 4,'X') 
277     mat_rot *= mathutils.Matrix.Rotation(rot[1],4,'Z')
278     mat_rot *= mathutils.Matrix.Rotation(rot[2], 4,'Y')
279     return mat_rot
280
281 #Feature use
282 def buildTrn_WorldMat( obj ):
283     # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix
284     # The rotation is applied at obejct level if there is any
285     loc,rot,scl=obj.matrix_world.decompose()
286     mat_trans = mathutils.Matrix.Translation( loc)
287     
288     return mat_trans
289
290 #Feature use
291 def buildScl_WorldMat( obj ):
292     # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix
293     # The rotation is applied at obejct level if there is any
294     loc,rot,scl=obj.matrix_world.decompose()
295     
296     mat_scale = mathutils.Matrix.Scale( scl[0], 4, ( 1, 0, 0 ) )
297     mat_scale *= mathutils.Matrix.Scale( scl[1], 4, ( 0, 1, 0 ) )
298     mat_scale *= mathutils.Matrix.Scale( scl[2], 4, ( 0, 0, 1 ) )
299     
300     return mat_scale
301     
302 def buildRot_World( obj ):
303     # This function builds a real world rotation values
304     loc,rot,scl=obj.matrix_world.decompose()
305     rot=rot.to_euler()
306     
307     return rot
308
309 def run( lat_props ):
310     
311     print("<-------------------------------->")
312     #obj = bpy.context.active_object
313     obj = bpy.context.object
314     
315     if obj.type == "MESH":
316         # set global property for the currently active latticed object
317         bpy.types.Scene.activelatticeobject = bpy.props.StringProperty( name = "currentlatticeobject", default = "" )
318         bpy.types.Scene.activelatticeobject = obj.name
319     
320         modifiersDelete( obj )
321         selvertsarray = selectedVerts_Grp( obj )
322         bbox = findBBox( obj, selvertsarray )
323         
324         size = bbox[2]
325         pos = bbox[3]
326         
327 #         print("lattce size, pos", size, " ", pos)
328         latticeDelete(obj)
329         lat = createLattice( obj, size, pos, lat_props )
330         
331         modif = obj.modifiers.new( "latticeeasytemp", "LATTICE" )
332         modif.object = lat
333         modif.vertex_group = "templatticegrp"
334         
335         bpy.context.scene.update()
336         bpy.ops.object.mode_set( mode = 'EDIT' )
337     
338     if obj.type == "LATTICE":
339         
340         if bpy.types.Scene.activelatticeobject:
341             name = bpy.types.Scene.activelatticeobject
342             print("last active latticed object", name)
343             
344             for ob in bpy.context.scene.objects:
345                 if ob.name == name:  # found the object with the lattice mod
346                     print("apply mod on", ob)
347                     object = ob
348                     modifiersApplyRemove(object)
349                     #modifiersDelete( object )  # apply the modifier and delete the lattice
350                     latticeDelete(obj)
351
352         
353     
354     
355     return
356
357
358 def main( context, latticeprops ):
359     run( latticeprops )
360
361 class EasyLattice( bpy.types.Operator ):
362     """Tooltip"""
363     bl_idname = "object.easy_lattice"
364     bl_label = "Easy Lattice Creator"
365     bl_space_type = "VIEW_3D"
366     bl_region_type = "TOOLS"
367     
368     lat_u = bpy.props.IntProperty( name = "Lattice u", default = 3 )
369     lat_w = bpy.props.IntProperty( name = "Lattice w", default = 3 )
370     lat_m = bpy.props.IntProperty( name = "Lattice m", default = 3 )
371     
372     lat_types = ( ( '0', 'KEY_LINEAR', '0' ), ( '1', 'KEY_CARDINAL', '1' ), ( '2', 'KEY_BSPLINE', '2' ) )
373     lat_type = bpy.props.EnumProperty( name = "Lattice Type", items = lat_types, default = '0' )
374     
375     
376     @classmethod
377     def poll( cls, context ):
378         return context.active_object is not None
379
380     def execute( self, context ):
381         
382         lat_u = self.lat_u
383         lat_w = self.lat_w
384         lat_m = self.lat_m
385         
386         # this is a reference to the "items" used to generate the
387         # enum property.
388         lat_type = self.lat_types[int( self.lat_type )][1]
389         lat_props = [lat_u, lat_w, lat_m, lat_type]
390
391         main( context, lat_props )
392         return {'FINISHED'}
393
394     def invoke( self, context, event ):
395         wm = context.window_manager
396         return wm.invoke_props_dialog( self )
397
398 def menu_draw( self, context ): 
399     self.layout.operator_context = 'INVOKE_REGION_WIN' 
400     self.layout.operator( EasyLattice.bl_idname, "Easy Lattice" ) 
401
402 def register():
403     bpy.utils.register_class( EasyLattice )
404     # bpy.utils.register
405     # menu_func = (lambda self, context: self.layout.operator('EasyLattice'))
406     # bpy.types.VIEW3D_PT_tools_objectmode.append(menu_draw)
407     bpy.types.VIEW3D_MT_edit_mesh_specials.append( menu_draw ) 
408
409
410 def unregister():
411     bpy.utils.unregister_class( EasyLattice )
412     # bpy.types.VIEW3D_PT_tools_objectmode.remove(menu_draw)
413     bpy.types.VIEW3D_MT_edit_mesh_specials.remove( menu_draw ) 
414
415 if __name__ == "__main__":
416     register()
417     # run()
418 #     bpy.ops.object.easy_lattice()
419
420
421
422