Skip to content
Snippets Groups Projects
mesh_carver.py 133 KiB
Newer Older
  • Learn to ignore specific revisions
  •         # 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 = 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 = Vector((0.0, 0.0, 0.0))
            self.SavCurLoc = 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.mesh_carver.ORandom
    
            self.ShowCursor = True
            self.ObjectMode = False
            self.ProfileMode = False
            self.Instantiate = context.scene.mesh_carver.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.display_type
                self.XRay = self.ObjectBrush.show_in_front
    
                # 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'}
    
    
        def CreateGeometry(self):
            context = bpy.context
            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 is 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
    
            # 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 is False) and (self.ProfileMode is 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 is 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:
    
                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')
    
    
                # Test if initial 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 is False) and (self.ForceRebool is False):
    
                    if self.ObjectMode or self.ProfileMode:
                        if self.BoolOps == UNION:
    
                            boolean_operation(bool_type="UNION")
    
                            boolean_operation(bool_type="DIFFERENCE")
    
                        boolean_operation(bool_type="DIFFERENCE")
    
                    if self.DontApply is 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 is 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 is True:
    
                            # Get the last object selected
                            lastSelected.append(context.selected_objects[0])
    
            context.scene.cursor_location = CursorLocation
    
    
            if self.DontApply is False:
    
                # Remove cut object
    
                if (self.ObjectMode is False) and (self.ProfileMode is False):
    
                    if len(context.selected_objects) > 0:
                        bpy.ops.object.select_all(action='TOGGLE')
                    bpy.data.objects[self.CurrentObj.name].select = True
                    bpy.ops.object.delete(use_global=False)
                else:
                    if self.ObjectMode:
    
                        self.ObjectBrush.display_type = self.ObjectBrush_DT
    
    
            if len(context.selected_objects) > 0:
                bpy.ops.object.select_all(action='TOGGLE')
    
    
            # Select cut 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)
    
    
            # Re-select initial objects
    
            bpy.ops.object.select_all(action='TOGGLE')
            if self.ObjectMode:
    
                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.display_type = "WIRE"
    
            if self.ProfileMode:
    
                self.ProfileBrush.display_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
    
    
    
    class CarverProperties(bpy.types.PropertyGroup):
        DepthCursor = BoolProperty(
            name="DepthCursor",
            default=False
        )
        OInstanciate = BoolProperty(
            name="Obj_Instantiate",
            default=False
        )
        ORandom = BoolProperty(
            name="Random_Rotation",
            default=False
        )
        DontApply = BoolProperty(
            name="Dont_Apply",
            default=False
    
        nProfile = IntProperty(
            name="Num_Profile",
            default=0
        )
    
    
    classes = (
        CarverPrefs,
        CarverProperties,
        Carver,
    )
    
    def register():
        for cls in classes:
            bpy.utils.register_class(cls)
    
        bpy.types.Scene.mesh_carver = PointerProperty(
            type=CarverProperties
        )
    
        # 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():
    
        for cls in classes:
            bpy.utils.unregister_class(cls)
    
        # remove keymap entry
    
        for km, kmi in addon_keymaps:
            km.keymap_items.remove(kmi)
        addon_keymaps.clear()
    
    
        del bpy.types.Scene.mesh_carver
    
    
    
    if __name__ == "__main__":
        register()