# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### bl_info = { "name": "Easy Lattice Object", "author": "Kursad Karatas", "version": (0, 5), "blender": (2, 66, 0), "location": "View3D > Easy Lattice", "description": "Create a lattice for shape editing", "warning": "", "wiki_url": "http://wiki.blender.org/index.php/Easy_Lattice_Editing_Addon", "tracker_url": "https://bitbucket.org/kursad/blender_addons_easylattice/src", "category": "Mesh"} import bpy import mathutils import math # Cleanup def modifiersDelete( obj ): for mod in obj.modifiers: print(mod) if mod.name == "latticeeasytemp": try: if mod.object == bpy.data.objects['LatticeEasytTemp']: print("applying modifier") bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name ) except: bpy.ops.object.modifier_remove( modifier = mod.name ) # Cleanup def modifiersApplyRemove( obj ): # print("passed object is", obj) # print("current object is", bpy.context.active_object) bpy.ops.object.select_all( action = 'DESELECT' ) bpy.ops.object.select_pattern(pattern=obj.name, extend=False) bpy.context.scene.objects.active=obj for mod in obj.modifiers: # print("modifier is ", mod) if mod.name == "latticeeasytemp": # try: if mod.object == bpy.data.objects['LatticeEasytTemp']: # print("mod object is ", mod.object) # print("applying modifier", mod," - ", mod.name) #obj.select= True # print("current object is", bpy.context.active_object) bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name ) #obj.modifiers.remove(mod) # except: # bpy.ops.object.modifier_remove( modifier = mod.name ) # Cleanup def latticeDelete(obj): bpy.ops.object.select_all( action = 'DESELECT' ) for ob in bpy.context.scene.objects: if "LatticeEasytTemp" in ob.name: ob.select = True bpy.ops.object.delete( use_global = False ) #select the original object back obj.select=True def createLattice( obj, size, pos, props ): # Create lattice and object lat = bpy.data.lattices.new( 'LatticeEasytTemp' ) ob = bpy.data.objects.new( 'LatticeEasytTemp', lat ) loc,rot,scl = getTransformations( obj ) #get the combined rotation matrix and apply to the lattice #ob.matrix_world=buildRot_WorldMat(obj)*ob.matrix_world #the position comes from the bbox ob.location = pos #ob.location=(pos.x+loc.x,pos.y+loc.y,pos.z+loc.z) #the size from bbox ob.scale = size #ob.scale=(size.x*scl.x, size.y*scl.y,size.z*scl.z) #the rotation comes from the combined obj world matrix which was converted to euler pairs. ob.rotation_euler = buildRot_World(obj) ob.show_x_ray = True # Link object to scene scn = bpy.context.scene scn.objects.link( ob ) scn.objects.active = ob scn.update() # Set lattice attributes lat.interpolation_type_u = props[3] lat.interpolation_type_v = props[3] lat.interpolation_type_w = props[3] lat.use_outside = False lat.points_u = props[0] lat.points_v = props[1] lat.points_w = props[2] #Set lattice points '''s = 0.0 points = [ (-s,-s,-s), (s,-s,-s), (-s,s,-s), (s,s,-s), (-s,-s,s), (s,-s,s), (-s,s,s), (s,s,s) ] for n,pt in enumerate(lat.points): for k in range(3): #pt.co[k] = points[n][k] ''' return ob def selectedVerts_Grp( obj ): # vertices=bpy.context.active_object.data.vertices vertices = obj.data.vertices selverts = [] if obj.mode == "EDIT": bpy.ops.object.editmode_toggle() for grp in obj.vertex_groups: if "templatticegrp" in grp.name: bpy.ops.object.vertex_group_set_active( group = grp.name ) bpy.ops.object.vertex_group_remove() tempgroup = obj.vertex_groups.new( "templatticegrp" ) # selverts=[vert for vert in vertices if vert.select==True] for vert in vertices: if vert.select == True: selverts.append( vert ) tempgroup.add( [vert.index], 1.0, "REPLACE" ) # print(selverts) return selverts def getTransformations( obj ): rot = obj.rotation_euler loc = obj.location size = obj.scale return [loc, rot, size] def findBBox( obj, selvertsarray ): # mat = buildTrnSclMat( obj ) mat =buildTrnScl_WorldMat(obj) mat_world = obj.matrix_world minx, miny, minz = selvertsarray[0].co maxx, maxy, maxz = selvertsarray[0].co c = 1 # for vert in selvertsarray: for c in range( len( selvertsarray ) ): # co=obj.matrix_world*vert.co.to_4d() # co = vert.co co = selvertsarray[c].co if co.x < minx: minx = co.x if co.y < miny: miny = co.y if co.z < minz: minz = co.z if co.x > maxx: maxx = co.x if co.y > maxy: maxy = co.y if co.z > maxz: maxz = co.z # print("local cord", selvertsarray[c].co) # print("world cord", co) c += 1 # print("total verts", len(selvertsarray)) # print("counted verts",c) # Based on world coords # print("-> minx miny minz",minx, miny, minz ) # print("-> maxx maxy maxz",maxx, maxy, maxz ) minpoint = mathutils.Vector( ( minx, miny, minz ) ) maxpoint = mathutils.Vector( ( maxx, maxy, maxz ) ) # middle point has to be calculated based on the real world matrix #middle = mat_world * mathutils.Vector((x_sum, y_sum, z_sum))/float(c) middle = ( ( minpoint + maxpoint ) / 2 ) minpoint = mat * minpoint # Calculate only based on loc/scale maxpoint = mat * maxpoint # Calculate only based on loc/scale middle = mat_world * middle # the middle has to be calculated based on the real world matrix size = maxpoint - minpoint size = mathutils.Vector( ( abs( size.x ), abs( size.y ), abs( size.z ) ) ) # local coords ##################################################### '''minpoint=mathutils.Vector((minx,miny,minz)) maxpoint=mathutils.Vector((maxx,maxy,maxz)) middle=mathutils.Vector( (x_sum/float(len(selvertsarray)), y_sum/float(len(selvertsarray)), z_sum/float(len(selvertsarray))) ) size=maxpoint-minpoint size=mathutils.Vector((abs(size.x),abs(size.y),abs(size.z))) ''' ##################################################### return [minpoint, maxpoint, size, middle ] def buildTrnSclMat( obj ): # This function builds a local matrix that encodes translation and scale and it leaves out the rotation matrix # The rotation is applied at obejct level if there is any mat_trans = mathutils.Matrix.Translation( obj.location ) mat_scale = mathutils.Matrix.Scale( obj.scale[0], 4, ( 1, 0, 0 ) ) mat_scale *= mathutils.Matrix.Scale( obj.scale[1], 4, ( 0, 1, 0 ) ) mat_scale *= mathutils.Matrix.Scale( obj.scale[2], 4, ( 0, 0, 1 ) ) mat_final = mat_trans * mat_scale return mat_final def buildTrnScl_WorldMat( obj ): # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix # The rotation is applied at obejct level if there is any loc,rot,scl=obj.matrix_world.decompose() mat_trans = mathutils.Matrix.Translation( loc) mat_scale = mathutils.Matrix.Scale( scl[0], 4, ( 1, 0, 0 ) ) mat_scale *= mathutils.Matrix.Scale( scl[1], 4, ( 0, 1, 0 ) ) mat_scale *= mathutils.Matrix.Scale( scl[2], 4, ( 0, 0, 1 ) ) mat_final = mat_trans * mat_scale return mat_final #Feature use def buildRot_WorldMat( obj ): # This function builds a real world matrix that encodes rotation and it leaves out translation and scale matrices loc,rot,scl=obj.matrix_world.decompose() rot=rot.to_euler() mat_rot = mathutils.Matrix.Rotation(rot[0], 4,'X') mat_rot *= mathutils.Matrix.Rotation(rot[1],4,'Z') mat_rot *= mathutils.Matrix.Rotation(rot[2], 4,'Y') return mat_rot #Feature use def buildTrn_WorldMat( obj ): # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix # The rotation is applied at obejct level if there is any loc,rot,scl=obj.matrix_world.decompose() mat_trans = mathutils.Matrix.Translation( loc) return mat_trans #Feature use def buildScl_WorldMat( obj ): # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix # The rotation is applied at obejct level if there is any loc,rot,scl=obj.matrix_world.decompose() mat_scale = mathutils.Matrix.Scale( scl[0], 4, ( 1, 0, 0 ) ) mat_scale *= mathutils.Matrix.Scale( scl[1], 4, ( 0, 1, 0 ) ) mat_scale *= mathutils.Matrix.Scale( scl[2], 4, ( 0, 0, 1 ) ) return mat_scale def buildRot_World( obj ): # This function builds a real world rotation values loc,rot,scl=obj.matrix_world.decompose() rot=rot.to_euler() return rot def run( lat_props ): # print("<-------------------------------->") #obj = bpy.context.active_object obj = bpy.context.object if obj.type == "MESH": # set global property for the currently active latticed object bpy.types.Scene.activelatticeobject = bpy.props.StringProperty( name = "currentlatticeobject", default = "" ) bpy.types.Scene.activelatticeobject = obj.name modifiersDelete( obj ) selvertsarray = selectedVerts_Grp( obj ) bbox = findBBox( obj, selvertsarray ) size = bbox[2] pos = bbox[3] # print("lattce size, pos", size, " ", pos) latticeDelete(obj) lat = createLattice( obj, size, pos, lat_props ) modif = obj.modifiers.new( "latticeeasytemp", "LATTICE" ) modif.object = lat modif.vertex_group = "templatticegrp" bpy.ops.object.select_all( action = 'DESELECT' ) bpy.ops.object.select_pattern(pattern=lat.name, extend=False) bpy.context.scene.objects.active=lat bpy.context.scene.update() bpy.ops.object.mode_set( mode = 'EDIT' ) if obj.type == "LATTICE": if bpy.types.Scene.activelatticeobject: name = bpy.types.Scene.activelatticeobject print("last active latticed object", name) #Are we in edit lattice mode? If so move on to object mode if obj.mode=="EDIT": bpy.ops.object.editmode_toggle() for ob in bpy.context.scene.objects: if ob.name == name: # found the object with the lattice mod print("apply mod on", ob) object = ob modifiersApplyRemove(object) #modifiersDelete( object ) # apply the modifier and delete the lattice latticeDelete(obj) return def main( context, latticeprops ): run( latticeprops ) class EasyLattice( bpy.types.Operator ): """Tooltip""" bl_idname = "object.easy_lattice" bl_label = "Easy Lattice Creator" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" lat_u = bpy.props.IntProperty( name = "Lattice u", default = 3 ) lat_w = bpy.props.IntProperty( name = "Lattice w", default = 3 ) lat_m = bpy.props.IntProperty( name = "Lattice m", default = 3 ) lat_types = ( ( '0', 'KEY_LINEAR', '0' ), ( '1', 'KEY_CARDINAL', '1' ), ( '2', 'KEY_BSPLINE', '2' ) ) lat_type = bpy.props.EnumProperty( name = "Lattice Type", items = lat_types, default = '0' ) @classmethod def poll( cls, context ): return context.active_object is not None def execute( self, context ): lat_u = self.lat_u lat_w = self.lat_w lat_m = self.lat_m # this is a reference to the "items" used to generate the # enum property. lat_type = self.lat_types[int( self.lat_type )][1] lat_props = [lat_u, lat_w, lat_m, lat_type] main( context, lat_props ) return {'FINISHED'} def invoke( self, context, event ): wm = context.window_manager return wm.invoke_props_dialog( self ) def menu_draw( self, context ): self.layout.operator_context = 'INVOKE_REGION_WIN' self.layout.operator( EasyLattice.bl_idname, "Easy Lattice" ) def register(): bpy.utils.register_class( EasyLattice ) # bpy.utils.register # menu_func = (lambda self, context: self.layout.operator('EasyLattice')) # bpy.types.VIEW3D_PT_tools_objectmode.append(menu_draw) bpy.types.VIEW3D_MT_edit_mesh_specials.append( menu_draw ) def unregister(): bpy.utils.unregister_class( EasyLattice ) # bpy.types.VIEW3D_PT_tools_objectmode.remove(menu_draw) bpy.types.VIEW3D_MT_edit_mesh_specials.remove( menu_draw ) if __name__ == "__main__": register() # run() # bpy.ops.object.easy_lattice()