Skip to content
Snippets Groups Projects
mesh_easylattice.py 13.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Kursad Karatas's avatar
    Kursad Karatas committed
    # ##### 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 #####
    
    
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    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"}
    
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    
    import bpy
    import mathutils
    import math
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    # Cleanup
    def modifiersDelete( obj ):
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        for mod in obj.modifiers:
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            if mod.name == "latticeeasytemp":
                try:
                    if mod.object == bpy.data.objects['LatticeEasytTemp']:
    
    Kursad Karatas's avatar
    Kursad Karatas committed
                        bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
                except:
                    bpy.ops.object.modifier_remove( modifier = mod.name )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    # Cleanup
    
    #     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
    
            if mod.name == "latticeeasytemp":
    #             try:
                if mod.object == bpy.data.objects['LatticeEasytTemp']:
    
    #                 print("mod object is ", mod.object)
    #                 print("applying modifier", mod," - ", mod.name)
    
    #                 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 )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        bpy.ops.object.select_all( action = 'DESELECT' )
        for ob in bpy.context.scene.objects:
             if "LatticeEasytTemp" in ob.name:
                 ob.select = True
    
        #select the original object back
        obj.select=True
    
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    def createLattice( obj, size, pos, props ):
        # Create lattice and object
        lat = bpy.data.lattices.new( 'LatticeEasytTemp' )
        ob = bpy.data.objects.new( 'LatticeEasytTemp', lat )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        loc,rot,scl = getTransformations( obj )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        #get the combined rotation matrix and apply to the lattice
        #ob.matrix_world=buildRot_WorldMat(obj)*ob.matrix_world
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        ob.location = pos
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            #ob.location=(pos.x+loc.x,pos.y+loc.y,pos.z+loc.z)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        ob.scale = size
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            #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.
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        ob.rotation_euler = buildRot_World(obj)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        ob.show_x_ray = True
        # Link object to scene
        scn = bpy.context.scene
        scn.objects.link( ob )
        scn.objects.active = ob
        scn.update()
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # Set lattice attributes
        lat.interpolation_type_u = props[3]
        lat.interpolation_type_v = props[3]
        lat.interpolation_type_w = props[3]
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        lat.use_outside = False
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        lat.points_u = props[0]
        lat.points_v = props[1]
        lat.points_w = props[2]
    
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        '''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]
        '''
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        return ob
    
    
    def selectedVerts_Grp( obj ):
    #     vertices=bpy.context.active_object.data.vertices
        vertices = obj.data.vertices
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        selverts = []
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        if obj.mode == "EDIT":
            bpy.ops.object.editmode_toggle()
    
        for grp in obj.vertex_groups:
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            if "templatticegrp" in grp.name:
                bpy.ops.object.vertex_group_set_active( group = grp.name )
                bpy.ops.object.vertex_group_remove()
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        tempgroup = obj.vertex_groups.new( "templatticegrp" )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # 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" )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # print(selverts)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        return selverts
    
    def getTransformations( obj ):
        rot = obj.rotation_euler
        loc = obj.location
        size = obj.scale
    
        return [loc, rot, size]
    
    def findBBox( obj, selvertsarray ):
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    #     mat = buildTrnSclMat( obj )
        mat =buildTrnScl_WorldMat(obj)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        mat_world = obj.matrix_world
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        minx, miny, minz = selvertsarray[0].co
        maxx, maxy, maxz = selvertsarray[0].co
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        c = 1
    #     for vert in selvertsarray:
        for c in range( len( selvertsarray ) ):
            # co=obj.matrix_world*vert.co.to_4d()
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    #         co = vert.co
            co = selvertsarray[c].co
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            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
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    #         print("local cord", selvertsarray[c].co)
    #         print("world cord", co)
            c += 1
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    #     print("total verts", len(selvertsarray))
    #     print("counted verts",c)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # Based on world coords
    #     print("-> minx miny minz",minx, miny, minz )
    #     print("-> maxx maxy maxz",maxx, maxy, maxz )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        minpoint = mathutils.Vector( ( minx, miny, minz ) )
        maxpoint = mathutils.Vector( ( maxx, maxy, maxz ) )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # middle point has to be calculated based on the real world matrix
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        #middle = mat_world * mathutils.Vector((x_sum, y_sum, z_sum))/float(c)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        size = maxpoint - minpoint
        size = mathutils.Vector( ( abs( size.x ), abs( size.y ), abs( size.z ) ) )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        #####################################################
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        '''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)))
        '''
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        #####################################################
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    
        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 ) )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        mat_final = mat_trans * mat_scale
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        return mat_final
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    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)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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 ) )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        mat_final = mat_trans * mat_scale
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        return mat_final
    
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    def buildRot_WorldMat( obj ):
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # This function builds a real world matrix that encodes rotation and it leaves out translation and scale matrices
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        loc,rot,scl=obj.matrix_world.decompose()
        rot=rot.to_euler()
    
    
        mat_rot = mathutils.Matrix.Rotation(rot[0], 4,'X')
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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()
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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 ) )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        return mat_scale
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    def buildRot_World( obj ):
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        # This function builds a real world rotation values
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        loc,rot,scl=obj.matrix_world.decompose()
        rot=rot.to_euler()
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        return rot
    
    def run( lat_props ):
    
    #     print("<-------------------------------->")
    
        #obj = bpy.context.active_object
        obj = bpy.context.object
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            modifiersDelete( obj )
            selvertsarray = selectedVerts_Grp( obj )
            bbox = findBBox( obj, selvertsarray )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            size = bbox[2]
            pos = bbox[3]
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    #         print("lattce size, pos", size, " ", pos)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            lat = createLattice( obj, size, pos, lat_props )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            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
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            bpy.context.scene.update()
            bpy.ops.object.mode_set( mode = 'EDIT' )
    
            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)
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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"
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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 )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        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' )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
        @classmethod
        def poll( cls, context ):
            return context.active_object is not None
    
        def execute( self, context ):
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            lat_u = self.lat_u
            lat_w = self.lat_w
            lat_m = self.lat_m
    
    Kursad Karatas's avatar
    Kursad Karatas committed
            # 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" )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    
    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 )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    
    
    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 )
    
    Kursad Karatas's avatar
    Kursad Karatas committed
    
    if __name__ == "__main__":
        register()
        # run()
    #     bpy.ops.object.easy_lattice()