1import bpy 2import bpy_extras 3import sys 4from bpy.props import ( 5 BoolProperty, 6 IntProperty, 7 PointerProperty, 8 StringProperty, 9 EnumProperty, 10 ) 11from mathutils import ( 12 Vector, 13 ) 14 15from bpy_extras.view3d_utils import ( 16 region_2d_to_vector_3d, 17 region_2d_to_origin_3d, 18 region_2d_to_location_3d, 19 location_3d_to_region_2d, 20) 21from .carver_profils import ( 22 Profils 23 ) 24 25from .carver_utils import ( 26 duplicateObject, 27 UndoListUpdate, 28 createMeshFromData, 29 SelectObject, 30 Selection_Save_Restore, 31 Selection_Save, 32 Selection_Restore, 33 update_grid, 34 objDiagonal, 35 Undo, 36 UndoAdd, 37 Pick, 38 rot_axis_quat, 39 MoveCursor, 40 Picking, 41 CreateCutSquare, 42 CreateCutCircle, 43 CreateCutLine, 44 boolean_operation, 45 update_bevel, 46 CreateBevel, 47 Rebool, 48 Snap_Cursor, 49 ) 50 51from .carver_draw import draw_callback_px 52 53# Modal Operator 54class CARVER_OT_operator(bpy.types.Operator): 55 bl_idname = "carver.operator" 56 bl_label = "Carver" 57 bl_description = "Cut or create Meshes in Object mode" 58 bl_options = {'REGISTER', 'UNDO'} 59 60 def __init__(self): 61 context = bpy.context 62 # Carve mode: Cut, Object, Profile 63 self.CutMode = False 64 self.CreateMode = False 65 self.ObjectMode = False 66 self.ProfileMode = False 67 68 # Create mode 69 self.ExclusiveCreateMode = False 70 if len(context.selected_objects) == 0: 71 self.ExclusiveCreateMode = True 72 self.CreateMode = True 73 74 # Cut type (Rectangle, Circle, Line) 75 self.rectangle = 0 76 self.line = 1 77 self.circle = 2 78 79 # Cut Rectangle coordinates 80 self.rectangle_coord = [] 81 82 # Selected type of cut 83 self.CutType = 0 84 85 # Boolean operation 86 self.difference = 0 87 self.union = 1 88 89 self.BoolOps = self.difference 90 91 self.CurrentSelection = context.selected_objects.copy() 92 self.CurrentActive = context.active_object 93 self.all_sel_obj_list = context.selected_objects.copy() 94 self.save_active_obj = None 95 96 args = (self, context) 97 self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') 98 99 self.mouse_path = [(0, 0), (0, 0)] 100 101 # Keyboard event 102 self.shift = False 103 self.ctrl = False 104 self.alt = False 105 106 self.dont_apply_boolean = context.scene.mesh_carver.DontApply 107 self.Auto_BevelUpdate = True 108 109 # Circle variables 110 self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90] 111 self.step = 4 112 113 # Primitives Position 114 self.xpos = 0 115 self.ypos = 0 116 self.InitPosition = False 117 118 # Close polygonal shape 119 self.Closed = False 120 121 # Depth Cursor 122 self.snapCursor = context.scene.mesh_carver.DepthCursor 123 124 # Help 125 self.AskHelp = False 126 127 # Working object 128 self.OpsObj = context.active_object 129 130 # Rebool forced (cut line) 131 self.ForceRebool = False 132 133 self.ViewVector = Vector() 134 self.CurrentObj = None 135 136 # Brush 137 self.BrushSolidify = False 138 self.WidthSolidify = False 139 self.CarveDepth = False 140 self.BrushDepth = False 141 self.BrushDepthOffset = 0.0 142 self.snap = False 143 144 self.ObjectScale = False 145 146 #Init create circle primitive 147 self.CLR_C = [] 148 149 # Cursor location 150 self.CurLoc = Vector((0.0, 0.0, 0.0)) 151 self.SavCurLoc = Vector((0.0, 0.0, 0.0)) 152 153 # Mouse region 154 self.mouse_region = -1, -1 155 self.SavMousePos = None 156 self.xSavMouse = 0 157 158 # Scale, rotate object 159 self.ascale = 0 160 self.aRotZ = 0 161 self.nRotZ = 0 162 self.quat_rot_axis = None 163 self.quat_rot = None 164 165 self.RandomRotation = context.scene.mesh_carver.ORandom 166 167 self.ShowCursor = True 168 169 self.Instantiate = context.scene.mesh_carver.OInstanciate 170 171 self.ProfileBrush = None 172 self.ObjectBrush = None 173 174 self.InitBrush = { 175 'location' : None, 176 'scale' : None, 177 'rotation_quaternion' : None, 178 'rotation_euler' : None, 179 'display_type' : 'WIRE', 180 'show_in_front' : False 181 } 182 183 # Array variables 184 self.nbcol = 1 185 self.nbrow = 1 186 self.gapx = 0 187 self.gapy = 0 188 self.scale_x = 1 189 self.scale_y = 1 190 self.GridScaleX = False 191 self.GridScaleY = False 192 193 @classmethod 194 def poll(cls, context): 195 ob = None 196 if len(context.selected_objects) > 0: 197 ob = context.selected_objects[0] 198 # Test if selected object or none (for create mode) 199 return ( 200 (ob and ob.type == 'MESH' and context.mode == 'OBJECT') or 201 (context.mode == 'OBJECT' and ob is None) or 202 (context.mode == 'EDIT_MESH')) 203 204 def modal(self, context, event): 205 PI = 3.14156 206 region_types = {'WINDOW', 'UI'} 207 win = context.window 208 209 # Find the limit of the view3d region 210 self.check_region(context,event) 211 212 for area in win.screen.areas: 213 if area.type == 'VIEW_3D': 214 for region in area.regions: 215 if not region_types or region.type in region_types: 216 region.tag_redraw() 217 218 # Change the snap increment value using the wheel mouse 219 if self.CutMode: 220 if self.alt is False: 221 if self.ctrl and (self.CutType in (self.line, self.rectangle)): 222 # Get the VIEW3D area 223 for i, a in enumerate(context.screen.areas): 224 if a.type == 'VIEW_3D': 225 space = context.screen.areas[i].spaces.active 226 grid_scale = space.overlay.grid_scale 227 grid_subdivisions = space.overlay.grid_subdivisions 228 229 if event.type == 'WHEELUPMOUSE': 230 space.overlay.grid_subdivisions += 1 231 elif event.type == 'WHEELDOWNMOUSE': 232 space.overlay.grid_subdivisions -= 1 233 234 if event.type in { 235 'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 236 'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_6', 237 'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9', 'NUMPAD_5'}: 238 return {'PASS_THROUGH'} 239 240 try: 241 # [Shift] 242 self.shift = True if event.shift else False 243 244 # [Ctrl] 245 self.ctrl = True if event.ctrl else False 246 247 # [Alt] 248 self.alt = False 249 250 # [Alt] press : Init position variable before moving the cut brush with LMB 251 if event.alt: 252 if self.InitPosition is False: 253 self.xpos = 0 254 self.ypos = 0 255 self.last_mouse_region_x = event.mouse_region_x 256 self.last_mouse_region_y = event.mouse_region_y 257 self.InitPosition = True 258 self.alt = True 259 260 # [Alt] release : update the coordinates 261 if self.InitPosition and self.alt is False: 262 for i in range(0, len(self.mouse_path)): 263 l = list(self.mouse_path[i]) 264 l[0] += self.xpos 265 l[1] += self.ypos 266 self.mouse_path[i] = tuple(l) 267 268 self.xpos = self.ypos = 0 269 self.InitPosition = False 270 271 if event.type == 'SPACE' and event.value == 'PRESS': 272 # If object or profile mode is TRUE : Confirm the cut 273 if self.ObjectMode or self.ProfileMode: 274 # If array, remove double with intersect meshes 275 if ((self.nbcol + self.nbrow) > 3): 276 # Go in edit mode mode 277 bpy.ops.object.mode_set(mode='EDIT') 278 # Remove duplicate vertices 279 bpy.ops.mesh.remove_doubles() 280 # Return in object mode 281 bpy.ops.object.mode_set(mode='OBJECT') 282 283 if self.alt: 284 # Save selected objects 285 self.all_sel_obj_list = context.selected_objects.copy() 286 if len(context.selected_objects) > 0: 287 bpy.ops.object.select_all(action='TOGGLE') 288 289 if self.ObjectMode: 290 SelectObject(self, self.ObjectBrush) 291 else: 292 SelectObject(self, self.ProfileBrush) 293 duplicateObject(self) 294 else: 295 # Brush Cut 296 self.Cut() 297 # Save selected objects 298 if self.ObjectMode: 299 if len(self.ObjectBrush.children) > 0: 300 self.all_sel_obj_list = context.selected_objects.copy() 301 if len(context.selected_objects) > 0: 302 bpy.ops.object.select_all(action='TOGGLE') 303 304 if self.ObjectMode: 305 SelectObject(self, self.ObjectBrush) 306 else: 307 SelectObject(self, self.ProfileBrush) 308 duplicateObject(self) 309 310 UndoListUpdate(self) 311 312 # Save cursor position 313 self.SavMousePos = self.CurLoc 314 else: 315 if self.CutMode is False: 316 # Cut Mode 317 self.CutType += 1 318 if self.CutType > 2: 319 self.CutType = 0 320 else: 321 if self.CutType == self.line: 322 # Cuts creation 323 CreateCutLine(self, context) 324 if self.CreateMode: 325 # Object creation 326 self.CreateGeometry() 327 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') 328 # Cursor Snap 329 context.scene.mesh_carver.DepthCursor = self.snapCursor 330 # Object Instantiate 331 context.scene.mesh_carver.OInstanciate = self.Instantiate 332 # Random rotation 333 context.scene.mesh_carver.ORandom = self.RandomRotation 334 335 return {'FINISHED'} 336 else: 337 self.Cut() 338 UndoListUpdate(self) 339 340 341#----------------------------------------------------- 342# Object creation 343#----------------------------------------------------- 344 345 346 # Object creation 347 if event.type == self.carver_prefs.Key_Create and event.value == 'PRESS': 348 if self.ExclusiveCreateMode is False: 349 self.CreateMode = not self.CreateMode 350 351 # Auto Bevel Update 352 if event.type == self.carver_prefs.Key_Update and event.value == 'PRESS': 353 self.Auto_BevelUpdate = not self.Auto_BevelUpdate 354 355 # Boolean operation type 356 if event.type == self.carver_prefs.Key_Bool and event.value == 'PRESS': 357 if (self.ProfileMode is True) or (self.ObjectMode is True): 358 if self.BoolOps == self.difference: 359 self.BoolOps = self.union 360 else: 361 self.BoolOps = self.difference 362 363 # Brush Mode 364 if event.type == self.carver_prefs.Key_Brush and event.value == 'PRESS': 365 self.dont_apply_boolean = False 366 if (self.ProfileMode is False) and (self.ObjectMode is False): 367 self.ProfileMode = True 368 else: 369 self.ProfileMode = False 370 if self.ObjectBrush is not None: 371 if self.ObjectMode is False: 372 self.ObjectMode = True 373 self.BrushSolidify = False 374 self.CList = self.OB_List 375 376 Selection_Save_Restore(self) 377 context.scene.mesh_carver.nProfile = self.nProfil 378 else: 379 self.ObjectMode = False 380 else: 381 self.BrushSolidify = False 382 Selection_Save_Restore(self) 383 384 if self.ProfileMode: 385 createMeshFromData(self) 386 self.ProfileBrush = bpy.data.objects["CT_Profil"] 387 Selection_Save(self) 388 self.BrushSolidify = True 389 390 bpy.ops.object.select_all(action='TOGGLE') 391 self.ProfileBrush.select_set(True) 392 context.view_layer.objects.active = self.ProfileBrush 393 # Set xRay 394 self.ProfileBrush.show_in_front = True 395 396 bpy.ops.object.modifier_add(type='SOLIDIFY') 397 context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" 398 context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 399 400 Selection_Restore(self) 401 402 self.CList = self.CurrentSelection 403 else: 404 if self.ObjectBrush is not None: 405 if self.ObjectMode is False: 406 if self.ObjectBrush is not None: 407 self.ObjectBrush.location = self.InitBrush['location'] 408 self.ObjectBrush.scale = self.InitBrush['scale'] 409 self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] 410 self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] 411 self.ObjectBrush.display_type = self.InitBrush['display_type'] 412 self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] 413 414 #Store active and selected objects 415 Selection_Save(self) 416 417 #Remove Carver modifier 418 self.BrushSolidify = False 419 bpy.ops.object.select_all(action='TOGGLE') 420 self.ObjectBrush.select_set(True) 421 context.view_layer.objects.active = self.ObjectBrush 422 bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") 423 424 #Restore selected and active object 425 Selection_Restore(self) 426 else: 427 if self.SolidifyPossible: 428 #Store active and selected objects 429 Selection_Save(self) 430 self.BrushSolidify = True 431 bpy.ops.object.select_all(action='TOGGLE') 432 self.ObjectBrush.select_set(True) 433 context.view_layer.objects.active = self.ObjectBrush 434 # Set xRay 435 self.ObjectBrush.show_in_front = True 436 bpy.ops.object.modifier_add(type='SOLIDIFY') 437 context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" 438 context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 439 440 #Restore selected and active object 441 Selection_Restore(self) 442 443 # Help display 444 if event.type == self.carver_prefs.Key_Help and event.value == 'PRESS': 445 self.AskHelp = not self.AskHelp 446 447 # Instantiate object 448 if event.type == self.carver_prefs.Key_Instant and event.value == 'PRESS': 449 self.Instantiate = not self.Instantiate 450 451 # Close polygonal shape 452 if event.type == self.carver_prefs.Key_Close and event.value == 'PRESS': 453 if self.CreateMode: 454 self.Closed = not self.Closed 455 456 if event.type == self.carver_prefs.Key_Apply and event.value == 'PRESS': 457 self.dont_apply_boolean = not self.dont_apply_boolean 458 459 # Scale object 460 if event.type == self.carver_prefs.Key_Scale and event.value == 'PRESS': 461 if self.ObjectScale is False: 462 self.mouse_region = event.mouse_region_x, event.mouse_region_y 463 self.ObjectScale = True 464 465 # Grid : Snap on grid 466 if event.type == self.carver_prefs.Key_Snap and event.value == 'PRESS': 467 self.snap = not self.snap 468 469 # Array : Add column 470 if event.type == 'UP_ARROW' and event.value == 'PRESS': 471 self.nbrow += 1 472 update_grid(self, context) 473 474 # Array : Delete column 475 elif event.type == 'DOWN_ARROW' and event.value == 'PRESS': 476 self.nbrow -= 1 477 update_grid(self, context) 478 479 # Array : Add row 480 elif event.type == 'RIGHT_ARROW' and event.value == 'PRESS': 481 self.nbcol += 1 482 update_grid(self, context) 483 484 # Array : Delete row 485 elif event.type == 'LEFT_ARROW' and event.value == 'PRESS': 486 self.nbcol -= 1 487 update_grid(self, context) 488 489 # Array : Scale gap between columns 490 if event.type == self.carver_prefs.Key_Gapy and event.value == 'PRESS': 491 if self.GridScaleX is False: 492 self.mouse_region = event.mouse_region_x, event.mouse_region_y 493 self.GridScaleX = True 494 495 # Array : Scale gap between rows 496 if event.type == self.carver_prefs.Key_Gapx and event.value == 'PRESS': 497 if self.GridScaleY is False: 498 self.mouse_region = event.mouse_region_x, event.mouse_region_y 499 self.GridScaleY = True 500 501 # Cursor depth or solidify pattern 502 if event.type == self.carver_prefs.Key_Depth and event.value == 'PRESS': 503 if (self.ObjectMode is False) and (self.ProfileMode is False): 504 self.snapCursor = not self.snapCursor 505 else: 506 # Solidify 507 508 if (self.ObjectMode or self.ProfileMode) and (self.SolidifyPossible): 509 solidify = True 510 511 if self.ObjectMode: 512 z = self.ObjectBrush.data.vertices[0].co.z 513 ErrorMarge = 0.01 514 for v in self.ObjectBrush.data.vertices: 515 if abs(v.co.z - z) > ErrorMarge: 516 solidify = False 517 self.CarveDepth = True 518 self.mouse_region = event.mouse_region_x, event.mouse_region_y 519 break 520 521 if solidify: 522 if self.ObjectMode: 523 for mb in self.ObjectBrush.modifiers: 524 if mb.type == 'SOLIDIFY': 525 AlreadySoldify = True 526 else: 527 for mb in self.ProfileBrush.modifiers: 528 if mb.type == 'SOLIDIFY': 529 AlreadySoldify = True 530 531 if AlreadySoldify is False: 532 Selection_Save(self) 533 self.BrushSolidify = True 534 535 bpy.ops.object.select_all(action='TOGGLE') 536 if self.ObjectMode: 537 self.ObjectBrush.select_set(True) 538 context.view_layer.objects.active = self.ObjectBrush 539 # Active le xray 540 self.ObjectBrush.show_in_front = True 541 else: 542 self.ProfileBrush.select_set(True) 543 context.view_layer.objects.active = self.ProfileBrush 544 # Active le xray 545 self.ProfileBrush.show_in_front = True 546 547 bpy.ops.object.modifier_add(type='SOLIDIFY') 548 context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" 549 550 context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 551 552 Selection_Restore(self) 553 554 self.WidthSolidify = not self.WidthSolidify 555 self.mouse_region = event.mouse_region_x, event.mouse_region_y 556 557 if event.type == self.carver_prefs.Key_BrushDepth and event.value == 'PRESS': 558 if self.ObjectMode: 559 self.CarveDepth = False 560 561 self.BrushDepth = True 562 self.mouse_region = event.mouse_region_x, event.mouse_region_y 563 564 # Random rotation 565 if event.type == 'R' and event.value == 'PRESS': 566 self.RandomRotation = not self.RandomRotation 567 568 # Undo 569 if event.type == 'Z' and event.value == 'PRESS': 570 if self.ctrl: 571 if (self.CutType == self.line) and (self.CutMode): 572 if len(self.mouse_path) > 1: 573 self.mouse_path[len(self.mouse_path) - 1:] = [] 574 else: 575 Undo(self) 576 577 # Mouse move 578 if event.type == 'MOUSEMOVE' : 579 if self.ObjectMode or self.ProfileMode: 580 fac = 50.0 581 if self.shift: 582 fac = 500.0 583 if self.WidthSolidify: 584 if self.ObjectMode: 585 bpy.data.objects[self.ObjectBrush.name].modifiers[ 586 "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac 587 elif self.ProfileMode: 588 bpy.data.objects[self.ProfileBrush.name].modifiers[ 589 "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac 590 self.mouse_region = event.mouse_region_x, event.mouse_region_y 591 elif self.CarveDepth: 592 for v in self.ObjectBrush.data.vertices: 593 v.co.z += (event.mouse_region_x - self.mouse_region[0]) / fac 594 self.mouse_region = event.mouse_region_x, event.mouse_region_y 595 elif self.BrushDepth: 596 self.BrushDepthOffset += (event.mouse_region_x - self.mouse_region[0]) / fac 597 self.mouse_region = event.mouse_region_x, event.mouse_region_y 598 else: 599 if (self.GridScaleX): 600 self.gapx += (event.mouse_region_x - self.mouse_region[0]) / 50 601 self.mouse_region = event.mouse_region_x, event.mouse_region_y 602 update_grid(self, context) 603 return {'RUNNING_MODAL'} 604 605 elif (self.GridScaleY): 606 self.gapy += (event.mouse_region_x - self.mouse_region[0]) / 50 607 self.mouse_region = event.mouse_region_x, event.mouse_region_y 608 update_grid(self, context) 609 return {'RUNNING_MODAL'} 610 611 elif self.ObjectScale: 612 self.ascale = -(event.mouse_region_x - self.mouse_region[0]) 613 self.mouse_region = event.mouse_region_x, event.mouse_region_y 614 615 if self.ObjectMode: 616 self.ObjectBrush.scale.x -= float(self.ascale) / 150.0 617 if self.ObjectBrush.scale.x <= 0.0: 618 self.ObjectBrush.scale.x = 0.0 619 self.ObjectBrush.scale.y -= float(self.ascale) / 150.0 620 if self.ObjectBrush.scale.y <= 0.0: 621 self.ObjectBrush.scale.y = 0.0 622 self.ObjectBrush.scale.z -= float(self.ascale) / 150.0 623 if self.ObjectBrush.scale.z <= 0.0: 624 self.ObjectBrush.scale.z = 0.0 625 626 elif self.ProfileMode: 627 if self.ProfileBrush is not None: 628 self.ProfileBrush.scale.x -= float(self.ascale) / 150.0 629 self.ProfileBrush.scale.y -= float(self.ascale) / 150.0 630 self.ProfileBrush.scale.z -= float(self.ascale) / 150.0 631 else: 632 if self.LMB: 633 if self.ctrl: 634 self.aRotZ = - \ 635 ((int((event.mouse_region_x - self.xSavMouse) / 10.0) * PI / 4.0) * 25.0) 636 else: 637 self.aRotZ -= event.mouse_region_x - self.mouse_region[0] 638 self.ascale = 0.0 639 640 self.mouse_region = event.mouse_region_x, event.mouse_region_y 641 else: 642 target_hit, target_normal, target_eul_rotation = Pick(context, event, self) 643 if target_hit is not None: 644 self.ShowCursor = True 645 up_vector = Vector((0.0, 0.0, 1.0)) 646 quat_rot_axis = rot_axis_quat(up_vector, target_normal) 647 self.quat_rot = target_eul_rotation @ quat_rot_axis 648 MoveCursor(quat_rot_axis, target_hit, self) 649 self.SavCurLoc = target_hit 650 if self.ctrl: 651 if self.SavMousePos is not None: 652 xEcart = abs(self.SavMousePos.x - self.SavCurLoc.x) 653 yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y) 654 zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z) 655 if (xEcart > yEcart) and (xEcart > zEcart): 656 self.CurLoc = Vector( 657 (target_hit.x, self.SavMousePos.y, self.SavMousePos.z)) 658 if (yEcart > xEcart) and (yEcart > zEcart): 659 self.CurLoc = Vector( 660 (self.SavMousePos.x, target_hit.y, self.SavMousePos.z)) 661 if (zEcart > xEcart) and (zEcart > yEcart): 662 self.CurLoc = Vector( 663 (self.SavMousePos.x, self.SavMousePos.y, target_hit.z)) 664 else: 665 self.CurLoc = target_hit 666 else: 667 self.CurLoc = target_hit 668 else: 669 if self.CutMode: 670 if self.alt is False: 671 if self.ctrl : 672 # Find the closest position on the overlay grid and snap the mouse on it 673 # Draw a mini grid around the cursor 674 mouse_pos = [[event.mouse_region_x, event.mouse_region_y]] 675 Snap_Cursor(self, context, event, mouse_pos) 676 677 else: 678 if len(self.mouse_path) > 0: 679 self.mouse_path[len(self.mouse_path) - 680 1] = (event.mouse_region_x, event.mouse_region_y) 681 else: 682 # [ALT] press, update position 683 self.xpos += (event.mouse_region_x - self.last_mouse_region_x) 684 self.ypos += (event.mouse_region_y - self.last_mouse_region_y) 685 686 self.last_mouse_region_x = event.mouse_region_x 687 self.last_mouse_region_y = event.mouse_region_y 688 689 elif event.type == 'LEFTMOUSE' and event.value == 'PRESS': 690 if self.ObjectMode or self.ProfileMode: 691 if self.LMB is False: 692 target_hit, target_normal, target_eul_rotation = Pick(context, event, self) 693 if target_hit is not None: 694 up_vector = Vector((0.0, 0.0, 1.0)) 695 self.quat_rot_axis = rot_axis_quat(up_vector, target_normal) 696 self.quat_rot = target_eul_rotation @ self.quat_rot_axis 697 self.mouse_region = event.mouse_region_x, event.mouse_region_y 698 self.xSavMouse = event.mouse_region_x 699 700 if self.ctrl: 701 self.nRotZ = int((self.aRotZ / 25.0) / (PI / 4.0)) 702 self.aRotZ = self.nRotZ * (PI / 4.0) * 25.0 703 704 self.LMB = True 705 706 # LEFTMOUSE 707 elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE' and self.in_view_3d: 708 if self.ObjectMode or self.ProfileMode: 709 # Rotation and scale 710 self.LMB = False 711 if self.ObjectScale is True: 712 self.ObjectScale = False 713 714 if self.GridScaleX is True: 715 self.GridScaleX = False 716 717 if self.GridScaleY is True: 718 self.GridScaleY = False 719 720 if self.WidthSolidify: 721 self.WidthSolidify = False 722 723 if self.CarveDepth is True: 724 self.CarveDepth = False 725 726 if self.BrushDepth is True: 727 self.BrushDepth = False 728 729 else: 730 if self.CutMode is False: 731 if self.ctrl: 732 Picking(context, event) 733 734 else: 735 736 if self.CutType == self.line: 737 if self.CutMode is False: 738 self.mouse_path.clear() 739 self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) 740 self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) 741 else: 742 self.mouse_path[0] = (event.mouse_region_x, event.mouse_region_y) 743 self.mouse_path[1] = (event.mouse_region_x, event.mouse_region_y) 744 self.CutMode = True 745 else: 746 if self.CutType != self.line: 747 # Cut creation 748 if self.CutType == self.rectangle: 749 CreateCutSquare(self, context) 750 if self.CutType == self.circle: 751 CreateCutCircle(self, context) 752 if self.CutType == self.line: 753 CreateCutLine(self, context) 754 755 if self.CreateMode: 756 # Object creation 757 self.CreateGeometry() 758 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') 759 # Depth Cursor 760 context.scene.mesh_carver.DepthCursor = self.snapCursor 761 # Instantiate object 762 context.scene.mesh_carver.OInstanciate = self.Instantiate 763 # Random rotation 764 context.scene.mesh_carver.ORandom = self.RandomRotation 765 # Apply operation 766 context.scene.mesh_carver.DontApply = self.dont_apply_boolean 767 768 # if Object mode, set initiale state 769 if self.ObjectBrush is not None: 770 self.ObjectBrush.location = self.InitBrush['location'] 771 self.ObjectBrush.scale = self.InitBrush['scale'] 772 self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] 773 self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] 774 self.ObjectBrush.display_type = self.InitBrush['display_type'] 775 self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] 776 777 # remove solidify 778 Selection_Save(self) 779 self.BrushSolidify = False 780 781 bpy.ops.object.select_all(action='TOGGLE') 782 self.ObjectBrush.select_set(True) 783 context.view_layer.objects.active = self.ObjectBrush 784 785 bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") 786 787 Selection_Restore(self) 788 789 context.scene.mesh_carver.nProfile = self.nProfil 790 791 return {'FINISHED'} 792 else: 793 self.Cut() 794 UndoListUpdate(self) 795 else: 796 # Line 797 self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) 798 799 # Change brush profil or circle subdivisions 800 elif (event.type == 'COMMA' and event.value == 'PRESS') or \ 801 (event.type == self.carver_prefs.Key_Subrem and event.value == 'PRESS'): 802 # Brush profil 803 if self.ProfileMode: 804 self.nProfil += 1 805 if self.nProfil >= self.MaxProfil: 806 self.nProfil = 0 807 createMeshFromData(self) 808 # Circle subdivisions 809 if self.CutType == self.circle: 810 self.step += 1 811 if self.step >= len(self.stepAngle): 812 self.step = len(self.stepAngle) - 1 813 # Change brush profil or circle subdivisions 814 elif (event.type == 'PERIOD' and event.value == 'PRESS') or \ 815 (event.type == self.carver_prefs.Key_Subadd and event.value == 'PRESS'): 816 # Brush profil 817 if self.ProfileMode: 818 self.nProfil -= 1 819 if self.nProfil < 0: 820 self.nProfil = self.MaxProfil - 1 821 createMeshFromData(self) 822 # Circle subdivisions 823 if self.CutType == self.circle: 824 if self.step > 0: 825 self.step -= 1 826 # Quit 827 elif event.type in {'RIGHTMOUSE', 'ESC'}: 828 # Depth Cursor 829 context.scene.mesh_carver.DepthCursor = self.snapCursor 830 # Instantiate object 831 context.scene.mesh_carver.OInstanciate = self.Instantiate 832 # Random Rotation 833 context.scene.mesh_carver.ORandom = self.RandomRotation 834 # Apply boolean operation 835 context.scene.mesh_carver.DontApply = self.dont_apply_boolean 836 837 # Reset Object 838 if self.ObjectBrush is not None: 839 self.ObjectBrush.location = self.InitBrush['location'] 840 self.ObjectBrush.scale = self.InitBrush['scale'] 841 self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] 842 self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] 843 self.ObjectBrush.display_type = self.InitBrush['display_type'] 844 self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] 845 846 # Remove solidify modifier 847 Selection_Save(self) 848 self.BrushSolidify = False 849 850 bpy.ops.object.select_all(action='TOGGLE') 851 self.ObjectBrush.select_set(True) 852 context.view_layer.objects.active = self.ObjectBrush 853 854 bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") 855 bpy.ops.object.select_all(action='TOGGLE') 856 857 Selection_Restore(self) 858 859 Selection_Save_Restore(self) 860 context.view_layer.objects.active = self.CurrentActive 861 context.scene.mesh_carver.nProfile = self.nProfil 862 863 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') 864 865 # Remove Copy Object Brush 866 if bpy.data.objects.get("CarverBrushCopy") is not None: 867 brush = bpy.data.objects["CarverBrushCopy"] 868 self.ObjectBrush.data = bpy.data.meshes[brush.data.name] 869 bpy.ops.object.select_all(action='DESELECT') 870 bpy.data.objects["CarverBrushCopy"].select_set(True) 871 bpy.ops.object.delete() 872 873 return {'FINISHED'} 874 875 return {'RUNNING_MODAL'} 876 877 except: 878 print("\n[Carver MT ERROR]\n") 879 import traceback 880 traceback.print_exc() 881 882 context.window.cursor_modal_set("DEFAULT") 883 context.area.header_text_set(None) 884 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') 885 886 self.report({'WARNING'}, 887 "Operation finished. Failure during Carving (Check the console for more info)") 888 889 return {'FINISHED'} 890 891 def cancel(self, context): 892 # Note: used to prevent memory leaks on quitting Blender while the modal operator 893 # is still running, gets called on return {"CANCELLED"} 894 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') 895 896 def invoke(self, context, event): 897 if context.area.type != 'VIEW_3D': 898 self.report({'WARNING'}, 899 "View3D not found or not currently active. Operation Cancelled") 900 self.cancel(context) 901 return {'CANCELLED'} 902 903 # test if some other object types are selected that are not meshes 904 for obj in context.selected_objects: 905 if obj.type != "MESH": 906 self.report({'WARNING'}, 907 "Some selected objects are not of the Mesh type. Operation Cancelled") 908 self.cancel(context) 909 return {'CANCELLED'} 910 911 if context.mode == 'EDIT_MESH': 912 bpy.ops.object.mode_set(mode='OBJECT') 913 914 #Load the Carver preferences 915 self.carver_prefs = bpy.context.preferences.addons[__package__].preferences 916 917 # Get default patterns 918 self.Profils = [] 919 for p in Profils: 920 self.Profils.append((p[0], p[1], p[2], p[3])) 921 922 for o in context.scene.objects: 923 if not o.name.startswith(self.carver_prefs.ProfilePrefix): 924 continue 925 # In-scene profiles may have changed, remove them to refresh 926 for m in bpy.data.meshes: 927 if m.name.startswith(self.carver_prefs.ProfilePrefix): 928 bpy.data.meshes.remove(m) 929 930 vertices = [] 931 for v in o.data.vertices: 932 vertices.append((v.co.x, v.co.y, v.co.z)) 933 934 faces = [] 935 for f in o.data.polygons: 936 face = [] 937 for v in f.vertices: 938 face.append(v) 939 940 faces.append(face) 941 942 self.Profils.append( 943 (o.name, 944 Vector((o.location.x, o.location.y, o.location.z)), 945 vertices, faces) 946 ) 947 948 self.nProfil = context.scene.mesh_carver.nProfile 949 self.MaxProfil = len(self.Profils) 950 951 952 # reset selected profile if last profile exceeds length of array 953 if self.nProfil >= self.MaxProfil: 954 self.nProfil = context.scene.mesh_carver.nProfile = 0 955 956 if len(context.selected_objects) > 1: 957 self.ObjectBrush = context.active_object 958 959 # Copy the brush object 960 ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy()) 961 ob.location = self.ObjectBrush.location 962 context.collection.objects.link(ob) 963 context.view_layer.update() 964 965 # Save default variables 966 self.InitBrush['location'] = self.ObjectBrush.location.copy() 967 self.InitBrush['scale'] = self.ObjectBrush.scale.copy() 968 self.InitBrush['rotation_quaternion'] = self.ObjectBrush.rotation_quaternion.copy() 969 self.InitBrush['rotation_euler'] = self.ObjectBrush.rotation_euler.copy() 970 self.InitBrush['display_type'] = self.ObjectBrush.display_type 971 self.InitBrush['show_in_front'] = self.ObjectBrush.show_in_front 972 973 # Test if flat object 974 z = self.ObjectBrush.data.vertices[0].co.z 975 ErrorMarge = 0.01 976 self.SolidifyPossible = True 977 for v in self.ObjectBrush.data.vertices: 978 if abs(v.co.z - z) > ErrorMarge: 979 self.SolidifyPossible = False 980 break 981 982 self.CList = [] 983 self.OPList = [] 984 self.RList = [] 985 self.OB_List = [] 986 987 for obj in context.selected_objects: 988 if obj != self.ObjectBrush: 989 self.OB_List.append(obj) 990 991 # Left button 992 self.LMB = False 993 994 # Undo Variables 995 self.undo_index = 0 996 self.undo_limit = context.preferences.edit.undo_steps 997 self.undo_list = [] 998 999 # Boolean operations type 1000 self.BooleanType = 0 1001 1002 self.UList = [] 1003 self.UList_Index = -1 1004 self.UndoOps = [] 1005 1006 context.window_manager.modal_handler_add(self) 1007 return {'RUNNING_MODAL'} 1008 1009 #Get the region area where the operator is used 1010 def check_region(self,context,event): 1011 if context.area != None: 1012 if context.area.type == "VIEW_3D" : 1013 for region in context.area.regions: 1014 if region.type == "TOOLS": 1015 t_panel = region 1016 elif region.type == "UI": 1017 ui_panel = region 1018 1019 view_3d_region_x = Vector((context.area.x + t_panel.width, context.area.x + context.area.width - ui_panel.width)) 1020 view_3d_region_y = Vector((context.region.y, context.region.y+context.region.height)) 1021 1022 if (event.mouse_x > view_3d_region_x[0] and event.mouse_x < view_3d_region_x[1] \ 1023 and event.mouse_y > view_3d_region_y[0] and event.mouse_y < view_3d_region_y[1]): 1024 self.in_view_3d = True 1025 else: 1026 self.in_view_3d = False 1027 else: 1028 self.in_view_3d = False 1029 1030 def CreateGeometry(self): 1031 context = bpy.context 1032 in_local_view = False 1033 1034 for area in context.screen.areas: 1035 if area.type == 'VIEW_3D': 1036 if area.spaces[0].local_view is not None: 1037 in_local_view = True 1038 1039 if in_local_view: 1040 bpy.ops.view3d.localview() 1041 1042 if self.ExclusiveCreateMode: 1043 # Default width 1044 objBBDiagonal = 0.5 1045 else: 1046 ActiveObj = self.CurrentSelection[0] 1047 if ActiveObj is not None: 1048 # Object dimensions 1049 objBBDiagonal = objDiagonal(ActiveObj) / 4 1050 subdivisions = 2 1051 1052 if len(context.selected_objects) > 0: 1053 bpy.ops.object.select_all(action='TOGGLE') 1054 1055 context.view_layer.objects.active = self.CurrentObj 1056 1057 bpy.data.objects[self.CurrentObj.name].select_set(True) 1058 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') 1059 1060 bpy.ops.object.mode_set(mode='EDIT') 1061 bpy.ops.mesh.select_all(action='SELECT') 1062 bpy.ops.mesh.select_mode(type="EDGE") 1063 if self.snapCursor is False: 1064 bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions) 1065 bpy.ops.mesh.extrude_region_move( 1066 TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2}) 1067 1068 bpy.ops.mesh.select_all(action='SELECT') 1069 bpy.ops.mesh.normals_make_consistent() 1070 bpy.ops.object.mode_set(mode='OBJECT') 1071 1072 saved_location_0 = context.scene.cursor.location.copy() 1073 bpy.ops.view3d.snap_cursor_to_active() 1074 saved_location = context.scene.cursor.location.copy() 1075 bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) 1076 context.scene.cursor.location = saved_location 1077 bpy.ops.object.origin_set(type='ORIGIN_CURSOR') 1078 context.scene.cursor.location = saved_location_0 1079 1080 bpy.data.objects[self.CurrentObj.name].select_set(True) 1081 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') 1082 1083 for o in self.all_sel_obj_list: 1084 bpy.data.objects[o.name].select_set(True) 1085 1086 if in_local_view: 1087 bpy.ops.view3d.localview() 1088 1089 self.CutMode = False 1090 self.mouse_path.clear() 1091 self.mouse_path = [(0, 0), (0, 0)] 1092 1093 def Cut(self): 1094 context = bpy.context 1095 1096 # Local view ? 1097 in_local_view = False 1098 for area in context.screen.areas: 1099 if area.type == 'VIEW_3D': 1100 if area.spaces[0].local_view is not None: 1101 in_local_view = True 1102 1103 if in_local_view: 1104 bpy.ops.view3d.localview() 1105 1106 # Save cursor position 1107 CursorLocation = context.scene.cursor.location.copy() 1108 1109 #List of selected objects 1110 selected_obj_list = [] 1111 1112 #Cut Mode with line 1113 if (self.ObjectMode is False) and (self.ProfileMode is False): 1114 1115 #Compute the bounding Box 1116 objBBDiagonal = objDiagonal(self.CurrentSelection[0]) 1117 if self.dont_apply_boolean: 1118 subdivisions = 1 1119 else: 1120 subdivisions = 32 1121 1122 # Get selected objects 1123 selected_obj_list = context.selected_objects.copy() 1124 1125 bpy.ops.object.select_all(action='TOGGLE') 1126 1127 context.view_layer.objects.active = self.CurrentObj 1128 1129 bpy.data.objects[self.CurrentObj.name].select_set(True) 1130 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') 1131 1132 bpy.ops.object.mode_set(mode='EDIT') 1133 bpy.ops.mesh.select_all(action='SELECT') 1134 bpy.ops.mesh.select_mode(type="EDGE") 1135 #Translate the created mesh away from the view 1136 if (self.snapCursor is False) or (self.ForceRebool): 1137 bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions) 1138 #Extrude the mesh region and move the result 1139 bpy.ops.mesh.extrude_region_move( 1140 TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2}) 1141 bpy.ops.mesh.select_all(action='SELECT') 1142 bpy.ops.mesh.normals_make_consistent() 1143 bpy.ops.object.mode_set(mode='OBJECT') 1144 else: 1145 # Create list 1146 if self.ObjectMode: 1147 for o in self.CurrentSelection: 1148 if o != self.ObjectBrush: 1149 selected_obj_list.append(o) 1150 self.CurrentObj = self.ObjectBrush 1151 else: 1152 selected_obj_list = self.CurrentSelection 1153 self.CurrentObj = self.ProfileBrush 1154 1155 for obj in self.CurrentSelection: 1156 UndoAdd(self, "MESH", obj) 1157 1158 # List objects create with rebool 1159 lastSelected = [] 1160 1161 for ActiveObj in selected_obj_list: 1162 context.scene.cursor.location = CursorLocation 1163 1164 if len(context.selected_objects) > 0: 1165 bpy.ops.object.select_all(action='TOGGLE') 1166 1167 # Select cut object 1168 bpy.data.objects[self.CurrentObj.name].select_set(True) 1169 context.view_layer.objects.active = self.CurrentObj 1170 1171 bpy.ops.object.mode_set(mode='EDIT') 1172 bpy.ops.mesh.select_all(action='SELECT') 1173 bpy.ops.object.mode_set(mode='OBJECT') 1174 1175 # Select object to cut 1176 bpy.data.objects[ActiveObj.name].select_set(True) 1177 context.view_layer.objects.active = ActiveObj 1178 1179 bpy.ops.object.mode_set(mode='EDIT') 1180 bpy.ops.mesh.select_all(action='DESELECT') 1181 bpy.ops.object.mode_set(mode='OBJECT') 1182 1183 # Boolean operation 1184 if (self.shift is False) and (self.ForceRebool is False): 1185 if self.ObjectMode or self.ProfileMode: 1186 if self.BoolOps == self.union: 1187 boolean_operation(bool_type="UNION") 1188 else: 1189 boolean_operation(bool_type="DIFFERENCE") 1190 else: 1191 boolean_operation(bool_type="DIFFERENCE") 1192 1193 # Apply booleans 1194 if self.dont_apply_boolean is False: 1195 BMname = "CT_" + self.CurrentObj.name 1196 for mb in ActiveObj.modifiers: 1197 if (mb.type == 'BOOLEAN') and (mb.name == BMname): 1198 try: 1199 bpy.ops.object.modifier_apply(modifier=BMname) 1200 except: 1201 bpy.ops.object.modifier_remove(modifier=BMname) 1202 exc_type, exc_value, exc_traceback = sys.exc_info() 1203 self.report({'ERROR'}, str(exc_value)) 1204 1205 bpy.ops.object.select_all(action='TOGGLE') 1206 else: 1207 if self.ObjectMode or self.ProfileMode: 1208 for mb in self.CurrentObj.modifiers: 1209 if (mb.type == 'SOLIDIFY') and (mb.name == "CT_SOLIDIFY"): 1210 try: 1211 bpy.ops.object.modifier_apply(modifier="CT_SOLIDIFY") 1212 except: 1213 exc_type, exc_value, exc_traceback = sys.exc_info() 1214 self.report({'ERROR'}, str(exc_value)) 1215 1216 # Rebool 1217 Rebool(context, self) 1218 1219 # Test if not empty object 1220 if context.selected_objects[0]: 1221 rebool_RT = context.selected_objects[0] 1222 if len(rebool_RT.data.vertices) > 0: 1223 # Create Bevel for new objects 1224 CreateBevel(context, context.selected_objects[0]) 1225 1226 UndoAdd(self, "REBOOL", context.selected_objects[0]) 1227 1228 context.scene.cursor.location = ActiveObj.location 1229 bpy.ops.object.origin_set(type='ORIGIN_CURSOR') 1230 else: 1231 bpy.ops.object.delete(use_global=False) 1232 1233 context.scene.cursor.location = CursorLocation 1234 1235 if self.ObjectMode: 1236 context.view_layer.objects.active = self.ObjectBrush 1237 if self.ProfileMode: 1238 context.view_layer.objects.active = self.ProfileBrush 1239 1240 if self.dont_apply_boolean is False: 1241 # Apply booleans 1242 BMname = "CT_" + self.CurrentObj.name 1243 for mb in ActiveObj.modifiers: 1244 if (mb.type == 'BOOLEAN') and (mb.name == BMname): 1245 try: 1246 bpy.ops.object.modifier_apply(modifier=BMname) 1247 except: 1248 bpy.ops.object.modifier_remove(modifier=BMname) 1249 exc_type, exc_value, exc_traceback = sys.exc_info() 1250 self.report({'ERROR'}, str(exc_value)) 1251 # Get new objects created with rebool operations 1252 if len(context.selected_objects) > 0: 1253 if self.shift is True: 1254 # Get the last object selected 1255 lastSelected.append(context.selected_objects[0]) 1256 1257 context.scene.cursor.location = CursorLocation 1258 1259 if self.dont_apply_boolean is False: 1260 # Remove cut object 1261 if (self.ObjectMode is False) and (self.ProfileMode is False): 1262 if len(context.selected_objects) > 0: 1263 bpy.ops.object.select_all(action='TOGGLE') 1264 bpy.data.objects[self.CurrentObj.name].select_set(True) 1265 bpy.ops.object.delete(use_global=False) 1266 else: 1267 if self.ObjectMode: 1268 self.ObjectBrush.display_type = self.InitBrush['display_type'] 1269 1270 if len(context.selected_objects) > 0: 1271 bpy.ops.object.select_all(action='TOGGLE') 1272 1273 # Select cut objects 1274 for obj in lastSelected: 1275 bpy.data.objects[obj.name].select_set(True) 1276 1277 for ActiveObj in selected_obj_list: 1278 bpy.data.objects[ActiveObj.name].select_set(True) 1279 context.view_layer.objects.active = ActiveObj 1280 # Update bevel 1281 list_act_obj = context.selected_objects.copy() 1282 if self.Auto_BevelUpdate: 1283 update_bevel(context) 1284 1285 # Re-select initial objects 1286 bpy.ops.object.select_all(action='TOGGLE') 1287 if self.ObjectMode: 1288 # Re-select brush 1289 self.ObjectBrush.select_set(True) 1290 for ActiveObj in selected_obj_list: 1291 bpy.data.objects[ActiveObj.name].select_set(True) 1292 context.view_layer.objects.active = ActiveObj 1293 1294 # If object has children, set "Wire" draw type 1295 if self.ObjectBrush is not None: 1296 if len(self.ObjectBrush.children) > 0: 1297 self.ObjectBrush.display_type = "WIRE" 1298 if self.ProfileMode: 1299 self.ProfileBrush.display_type = "WIRE" 1300 1301 if in_local_view: 1302 bpy.ops.view3d.localview() 1303 1304 # Reset variables 1305 self.CutMode = False 1306 self.mouse_path.clear() 1307 self.mouse_path = [(0, 0), (0, 0)] 1308 1309 self.ForceRebool = False 1310 1311 # bpy.ops.mesh.customdata_custom_splitnormals_clear() 1312 1313 1314class CarverProperties(bpy.types.PropertyGroup): 1315 DepthCursor: BoolProperty( 1316 name="DepthCursor", 1317 default=False 1318 ) 1319 OInstanciate: BoolProperty( 1320 name="Obj_Instantiate", 1321 default=False 1322 ) 1323 ORandom: BoolProperty( 1324 name="Random_Rotation", 1325 default=False 1326 ) 1327 DontApply: BoolProperty( 1328 name="Dont_Apply", 1329 default=False 1330 ) 1331 nProfile: IntProperty( 1332 name="Num_Profile", 1333 default=0 1334 ) 1335 1336 1337def register(): 1338 from bpy.utils import register_class 1339 bpy.utils.register_class(CARVER_OT_operator) 1340 bpy.utils.register_class(CarverProperties) 1341 bpy.types.Scene.mesh_carver = bpy.props.PointerProperty(type=CarverProperties) 1342 1343def unregister(): 1344 from bpy.utils import unregister_class 1345 bpy.utils.unregister_class(CarverProperties) 1346 bpy.utils.unregister_class(CARVER_OT_operator) 1347 del bpy.types.Scene.mesh_carver 1348