Skip to content
Snippets Groups Projects
mesh_carver.py 140 KiB
Newer Older
  • Learn to ignore specific revisions
  •             win = context.window
    
                # Get default patterns
                self.Profils = []
                for p in Profils:
                    self.Profils.append((p[0], p[1], p[2], p[3]))
    
    
                for o in context.scene.objects:
                    if not o.name.startswith(context.scene.ProfilePrefix):
                        continue
    
                    # In-scene profiles may have changed, remove them to refresh
                    for m in bpy.data.meshes:
                        if m.name.startswith(context.scene.ProfilePrefix):
                            bpy.data.meshes.remove(m)
    
                    vertices = []
                    for v in o.data.vertices:
                        vertices.append((v.co.x, v.co.y, v.co.z))
    
                    faces = []
                    for f in o.data.polygons:
                        face = []
    
                        for v in f.vertices:
                            face.append(v)
    
                        faces.append(face)
    
                    self.Profils.append((o.name, mathutils.Vector((o.location.x, o.location.y, o.location.z)), vertices, faces))
    
    
                self.nProfil = context.scene.nProfile
                self.MaxProfil = len(self.Profils)
    
    
                # reset selected profile if last profile exceeds length of array
                if self.nProfil >= self.MaxProfil:
                  self.nProfil = context.scene.nProfile = 0
    
    
                # Save selection
                self.CurrentSelection = context.selected_objects.copy()
                self.CurrentActive = context.active_object
                self.SavSel = context.selected_objects.copy()
                self.Sav_ac = None
    
                args = (self, context)
    
                self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
    
                self.mouse_path = [(0, 0), (0, 0)]
    
                self.shift = False
                self.ctrl = False
                self.alt = False
    
                self.bDone = False
    
                self.DontApply = context.scene.DontApply
                self.Auto_BevelUpdate = True
    
                # Cut type (Rectangle, Circle, Line)
                self.CutMode = 0
                self.BoolOps = DIFFERENCE
    
                # Circle variables
                self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90]
                self.step = 4
                self.stepRotation = 0
    
                # Primitives Position
                self.xpos = 0
                self.ypos = 0
                self.InitPosition = False
    
                # Line Increment
                self.Increment = 15
                # Close polygonal shape
                self.Closed = False
    
                # Depth Cursor
                self.snapCursor = context.scene.DepthCursor
    
                # Help
                self.AskHelp = False
    
                # Working object
                self.OpsObj = context.active_object
    
                # Create mode
                self.CreateMode = False
                self.ExclusiveCreateMode = False
                if len(context.selected_objects) == 0:
                    self.ExclusiveCreateMode = True
                    self.CreateMode = True
    
                # Rebool forced (cut line)
                self.ForceRebool = False
    
                self.ViewVector = mathutils.Vector()
    
                self.CurrentObj = None
    
                # Brush
                self.BrushSolidify = False
                self.WidthSolidify = False
    
                self.CarveDepth = False
    
                self.BrushDepth = False
    
                self.BrushDepthOffset = 0.0
    
    
                self.ObjectScale = False
    
                self.CircleListRaw = []
                self.CLR_C = []
                self.CurLoc = mathutils.Vector((0.0, 0.0, 0.0))
                self.SavCurLoc = mathutils.Vector((0.0, 0.0, 0.0))
                self.CRadius = 1.0
                CreatePrimitive(self, 10.0, 1.0)
                self.VertsList = []
                self.FacesList = []
    
                self.am = -1, -1
                self.SavMousePos = None
                self.xSavMouse = 0
    
                self.ascale = 0
                self.aRotZ = 0
                self.nRotZ = 0
                self.aqR = None
                self.qRot = None
    
                self.RandomRotation = context.scene.ORandom
    
                self.ShowCursor = True
    
                self.ObjectMode = False
                self.ProfileMode = False
                self.Instantiate = context.scene.OInstanciate
    
                self.ProfileBrush = None
                self.ObjectBrush = None
                self.InitBrushPosition = None
                self.InitBrushScale = None
                self.InitBrushQRotation = None
                self.InitBrushERotation = None
                self.InitBrushARotation = None
    
                self.ObjectBrush_DT = "WIRE"
                self.XRay = False
    
                # Grid mesh
                self.nbcol = 1
                self.nbrow = 1
                self.gapx = 0
                self.gapy = 0
                self.scale_x = 1
                self.scale_y = 1
    
                self.GridScaleX = False
                self.GridScaleY = False
    
                if len(context.selected_objects) > 1:
                    self.ObjectBrush = context.active_object
    
    
                    # Copy the brush object
    
                    ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy())
                    ob.location = self.ObjectBrush.location
                    scene = context.scene
                    scene.objects.link(ob)
                    scene.update()
    
                    # Get default variables
                    self.InitBrushPosition = self.ObjectBrush.location.copy()
                    self.InitBrushScale = self.ObjectBrush.scale.copy()
                    self.InitBrushQRotation = self.ObjectBrush.rotation_quaternion.copy()
                    self.InitBrushERotation = self.ObjectBrush.rotation_euler.copy()
                    self.ObjectBrush_DT = self.ObjectBrush.draw_type
                    self.XRay = self.ObjectBrush.show_x_ray
                    # Test if flat object
                    z = self.ObjectBrush.data.vertices[0].co.z
                    ErrorMarge = 0.01
                    self.Solidify_Active_Start = True
                    for v in self.ObjectBrush.data.vertices:
                        if abs(v.co.z - z) > ErrorMarge:
                            self.Solidify_Active_Start = False
                            break
                    self.SolidifyPossible = False
    
                self.CList = []
                self.OPList = []
                self.RList = []
                self.OB_List = []
    
                for ent in context.selected_objects:
                    if ent != self.ObjectBrush:
                        self.OB_List.append(ent)
    
                # Left button
                self.LMB = False
    
                # Undo Variables
                self.undo_index = 0
                self.undo_limit = context.user_preferences.edit.undo_steps
                self.undo_list = []
    
                # Boolean operations type
                self.BooleanType = 0
    
                self.UList = []
                self.UList_Index = -1
                self.UndoOps = []
    
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
            else:
                self.report({'WARNING'}, "View3D not found, cannot run operator")
                return {'CANCELLED'}
    
        # --------------------------------------------------------------------------------------------------
    
        # --------------------------------------------------------------------------------------------------
    
        def CreateGeometry(self):
            context = bpy.context
    
            region_id = context.region.id
    
            bLocalView = False
            for area in context.screen.areas:
                if area.type == 'VIEW_3D':
                    if area.spaces[0].local_view is not None:
                        bLocalView = True
    
            if bLocalView:
                bpy.ops.view3d.localview()
    
            if self.ExclusiveCreateMode:
                # Default width
                objBBDiagonal = 0.5
            else:
                ActiveObj = self.CurrentSelection[0]
                if ActiveObj is not None:
                    objBBDiagonal = objDiagonal(ActiveObj) / 4
            subdivisions = 2
    
            if len(context.selected_objects) > 0:
                bpy.ops.object.select_all(action='TOGGLE')
    
            context.scene.objects.active = self.CurrentObj
    
            bpy.data.objects[self.CurrentObj.name].select = True
            bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
    
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.select_all(action='SELECT')
            bpy.ops.mesh.select_mode(type="EDGE")
            if self.snapCursor == False:
                bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions)
            bpy.ops.mesh.extrude_region_move(
                TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2})
    
            bpy.ops.mesh.select_all(action='SELECT')
            bpy.ops.mesh.normals_make_consistent()
            bpy.ops.object.mode_set(mode='OBJECT')
    
            saved_location_0 = context.scene.cursor_location.copy()
            bpy.ops.view3d.snap_cursor_to_active()
            saved_location = context.scene.cursor_location.copy()
            bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
            context.scene.cursor_location = saved_location
            bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
            context.scene.cursor_location = saved_location_0
    
            bpy.data.objects[self.CurrentObj.name].select = True
            bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
    
            for o in self.SavSel:
                bpy.data.objects[o.name].select = True
    
            if bLocalView:
                bpy.ops.view3d.localview()
    
            self.bDone = False
            self.mouse_path.clear()
            self.mouse_path = [(0, 0), (0, 0)]
    
        # --------------------------------------------------------------------------------------------------
    
        # --------------------------------------------------------------------------------------------------
    
        def Cut(self):
            context = bpy.context
    
            UNDO = []
    
            # Local view ?
            bLocalView = False
            for area in context.screen.areas:
                if area.type == 'VIEW_3D':
                    if area.spaces[0].local_view is not None:
                        bLocalView = True
    
            if bLocalView:
                bpy.ops.view3d.localview()
    
            # Save cursor position
            CursorLocation = context.scene.cursor_location.copy()
    
            ActiveObjList = []
            if (self.ObjectMode == False) and (self.ProfileMode == False):
                objBBDiagonal = objDiagonal(self.CurrentSelection[0])
                subdivisions = 32
                if self.DontApply:
                    subdivisions = 1
    
                # Get selected objects
                ActiveObjList = context.selected_objects.copy()
    
                bpy.ops.object.select_all(action='TOGGLE')
    
                context.scene.objects.active = self.CurrentObj
    
                bpy.data.objects[self.CurrentObj.name].select = True
                bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
    
                bpy.ops.object.mode_set(mode='EDIT')
                bpy.ops.mesh.select_all(action='SELECT')
                bpy.ops.mesh.select_mode(type="EDGE")
                if (self.snapCursor == False) or (self.ForceRebool):
                    bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions)
                bpy.ops.mesh.extrude_region_move(
                    TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2})
                bpy.ops.mesh.select_all(action='SELECT')
                bpy.ops.mesh.normals_make_consistent()
                bpy.ops.object.mode_set(mode='OBJECT')
            else:
                # Create liste
                if self.ObjectMode:
                    for o in self.CurrentSelection:
                        if o != self.ObjectBrush:
                            ActiveObjList.append(o)
                    self.CurrentObj = self.ObjectBrush
                else:
                    ActiveObjList = self.CurrentSelection
                    self.CurrentObj = self.ProfileBrush
    
            for o in self.CurrentSelection:
                UndoAdd(self, "MESH", o)
    
            # List objects create with rebool
            lastSelected = []
    
            for ActiveObj in ActiveObjList:
                context.scene.cursor_location = CursorLocation
    
                if len(context.selected_objects) > 0:
                    bpy.ops.object.select_all(action='TOGGLE')
    
                # Testif intitiale object has bevel
                BevelAO = False
                for obj in ActiveObjList:
                    for mb in obj.modifiers:
                        if mb.type == 'BEVEL':
                            BevelAO = True
    
                # Select cut object
                bpy.data.objects[self.CurrentObj.name].select = True
                context.scene.objects.active = self.CurrentObj
    
                bpy.ops.object.mode_set(mode='EDIT')
                bpy.ops.mesh.select_all(action='SELECT')
                bpy.ops.object.mode_set(mode='OBJECT')
    
                # Select object to cut
                bpy.data.objects[ActiveObj.name].select = True
                context.scene.objects.active = ActiveObj
    
                bpy.ops.object.mode_set(mode='EDIT')
                bpy.ops.mesh.select_all(action='DESELECT')
                bpy.ops.object.mode_set(mode='OBJECT')
    
                # Boolean operation
                if (self.shift == False) and (self.ForceRebool == False):
                    if self.ObjectMode or self.ProfileMode:
                        if self.BoolOps == UNION:
                            # Union
                            boolean_union()
                        else:
                            # Cut object
                            boolean_difference()
                    else:
                        # Cut
                        boolean_difference()
    
                    # Apply booleans
                    if self.DontApply == False:
                        BMname = "CT_" + self.CurrentObj.name
                        for mb in ActiveObj.modifiers:
                            if (mb.type == 'BOOLEAN') and (mb.name == BMname):
                                try:
                                    bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname)
                                except:
                                    bpy.ops.object.modifier_remove(modifier=BMname)
                                    exc_type, exc_value, exc_traceback = sys.exc_info()
                                    self.report({'ERROR'}, str(exc_value))
    
                    bpy.ops.object.select_all(action='TOGGLE')
                else:
                    if self.ObjectMode or self.ProfileMode:
                        for mb in self.CurrentObj.modifiers:
                            if (mb.type == 'SOLIDIFY') and (mb.name == "CT_SOLIDIFY"):
                                try:
                                    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
                                except:
                                    exc_type, exc_value, exc_traceback = sys.exc_info()
                                    self.report({'ERROR'}, str(exc_value))
    
                    # Rebool
                    Rebool(context, self)
                    # Test if not empty object
                    if context.selected_objects[0]:
                        rebool_RT = context.selected_objects[0]
                        if len(rebool_RT.data.vertices) > 0:
                            # Create Bevel for new objects
                            if BevelAO:
                                CreateBevel(context, context.selected_objects[0])
                            UndoAdd(self, "REBOOL", context.selected_objects[0])
    
                            context.scene.cursor_location = ActiveObj.location
                            bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
                        else:
                            bpy.ops.object.delete(use_global=False)
    
                    context.scene.cursor_location = CursorLocation
    
                    if self.ObjectMode:
                        context.scene.objects.active = self.ObjectBrush
                    if self.ProfileMode:
                        context.scene.objects.active = self.ProfileBrush
    
                if self.DontApply == False:
                    # Apply booleans
                    BMname = "CT_" + self.CurrentObj.name
                    for mb in ActiveObj.modifiers:
                        if (mb.type == 'BOOLEAN') and (mb.name == BMname):
                            try:
                                bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname)
                            except:
                                bpy.ops.object.modifier_remove(modifier=BMname)
                                exc_type, exc_value, exc_traceback = sys.exc_info()
                                self.report({'ERROR'}, str(exc_value))
                    # Get new objects created with rebool operations
                    if len(context.selected_objects) > 0:
                        if self.shift == True:
                            # Get the last object selected
                            lastSelected.append(context.selected_objects[0])
    
            context.scene.cursor_location = CursorLocation
    
            if self.DontApply == False:
                # Remove cut object
                if (self.ObjectMode == False) and (self.ProfileMode == False):
                    if len(context.selected_objects) > 0:
                        bpy.ops.object.select_all(action='TOGGLE')
                    bpy.data.objects[self.CurrentObj.name].select = True
                    cname = self.CurrentObj.name
                    bpy.ops.object.delete(use_global=False)
                else:
                    if self.ObjectMode:
                        self.ObjectBrush.draw_type = self.ObjectBrush_DT
    
            if len(context.selected_objects) > 0:
                bpy.ops.object.select_all(action='TOGGLE')
    
            # Select cutted objects
            for obj in lastSelected:
                bpy.data.objects[obj.name].select = True
    
            for ActiveObj in ActiveObjList:
                bpy.data.objects[ActiveObj.name].select = True
                context.scene.objects.active = ActiveObj
            # Update bevel
            list_act_obj = context.selected_objects.copy()
            if self.Auto_BevelUpdate:
                update_bevel(context)
    
            # Reselect intiale objects
            bpy.ops.object.select_all(action='TOGGLE')
            if self.ObjectMode:
                # Reselect brush
                self.ObjectBrush.select = True
            for ActiveObj in ActiveObjList:
                bpy.data.objects[ActiveObj.name].select = True
                context.scene.objects.active = ActiveObj
    
            # If object has children, set "Wire" draw type
            if self.ObjectBrush is not None:
                if len(self.ObjectBrush.children) > 0:
                    self.ObjectBrush.draw_type = "WIRE"
            if self.ProfileMode:
                self.ProfileBrush.draw_type = "WIRE"
    
            if bLocalView:
                bpy.ops.view3d.localview()
    
            # Reset variables
            self.bDone = False
            self.mouse_path.clear()
            self.mouse_path = [(0, 0), (0, 0)]
    
            self.ForceRebool = False
    
    
    classes = (
        Carver,
        )
    
    addon_keymaps = []
    
    
    def register():
        bpy.types.Scene.DepthCursor = bpy.props.BoolProperty(name="DepthCursor", default=False)
        bpy.types.Scene.OInstanciate = bpy.props.BoolProperty(name="Obj_Instantiate", default=False)
        bpy.types.Scene.ORandom = bpy.props.BoolProperty(name="Random_Rotation", default=False)
        bpy.types.Scene.DontApply = bpy.props.BoolProperty(name="Dont_Apply", default=False)
        bpy.types.Scene.nProfile = bpy.props.IntProperty(name="Num_Profile", default=0)
    
        bpy.utils.register_class(CarverPrefs)
    
        bpy.utils.register_class(Carver)
        # add keymap entry
        kcfg = bpy.context.window_manager.keyconfigs.addon
        if kcfg:
            km = kcfg.keymaps.new(name='3D View', space_type='VIEW_3D')
            kmi = km.keymap_items.new("object.carver", 'X', 'PRESS', shift=True, ctrl=True)
            addon_keymaps.append((km, kmi))
    
    
    def unregister():
        bpy.utils.unregister_class(CarverPrefs)
    
    
        # remove keymap entry
    
        for km, kmi in addon_keymaps:
            km.keymap_items.remove(kmi)
        addon_keymaps.clear()
    
        bpy.utils.unregister_class(Carver)
    
    
    if __name__ == "__main__":
        register()