@ -8,19 +8,19 @@ from random import random
from . import utilities_ui
from . import settings
from . import utilities_bake as ub # Use shorthand ub = utitlites_bake
from . import utilities_bake as ub # Use shorthand ub = utitlites_bake
# Notes: https://docs.blender.org/manual/en/dev/render/blender_render/bake.html
modes = {
modes = {
' normal_tangent ' : ub . BakeMode ( ' ' , type = ' NORMAL ' , color = ( 0.5 , 0.5 , 1 , 1 ) , use_project = True ) ,
' normal_object ' : ub . BakeMode ( ' ' , type = ' NORMAL ' , color = ( 0.5 , 0.5 , 1 , 1 ) , normal_space = ' OBJECT ' ) ,
' normal_object ' : ub . BakeMode ( ' ' , type = ' NORMAL ' , color = ( 0.5 , 0.5 , 1 , 1 ) , normal_space = ' OBJECT ' ) ,
' cavity ' : ub . BakeMode ( ' bake_cavity ' , type = ' EMIT ' , setVColor = ub . setup_vertex_color_dirty ) ,
' paint_base ' : ub . BakeMode ( ' bake_paint_base ' , type = ' EMIT ' ) ,
' dust ' : ub . BakeMode ( ' bake_dust ' , type = ' EMIT ' , setVColor = ub . setup_vertex_color_dirty ) ,
' id_element ' : ub . BakeMode ( ' bake_vertex_color ' , type = ' EMIT ' , setVColor = ub . setup_vertex_color_id_element ) ,
' id_material ' : ub . BakeMode ( ' bake_vertex_color ' , type = ' EMIT ' , setVColor = ub . setup_vertex_color_id_material ) ,
' selection ' : ub . BakeMode ( ' bake_vertex_color ' , type = ' EMIT ' , color = ( 0 , 0 , 0 , 1 ) , setVColor = ub . setup_vertex_color_selection ) ,
' id_element ' : ub . BakeMode ( ' bake_vertex_color ' , type = ' EMIT ' , setVColor = ub . setup_vertex_color_id_element ) ,
' id_material ' : ub . BakeMode ( ' bake_vertex_color ' , type = ' EMIT ' , setVColor = ub . setup_vertex_color_id_material ) ,
' selection ' : ub . BakeMode ( ' bake_vertex_color ' , type = ' EMIT ' , color = ( 0 , 0 , 0 , 1 ) , setVColor = ub . setup_vertex_color_selection ) ,
' diffuse ' : ub . BakeMode ( ' ' , type = ' DIFFUSE ' ) ,
# 'displacment': ub.BakeMode('', type='DISPLACEMENT', use_project=True, color=(0, 0, 0, 1), engine='CYCLES'),
' ao ' : ub . BakeMode ( ' ' , type = ' AO ' , color = ( 1 , 1 , 1 , 0 ) , params = [ " bake_samples " ] , engine = ' CYCLES ' ) ,
@ -30,12 +30,14 @@ modes={
' wireframe ' : ub . BakeMode ( ' bake_wireframe ' , type = ' EMIT ' , color = ( 0 , 0 , 0 , 1 ) , params = [ " bake_wireframe_size " ] )
}
if hasattr ( bpy . types , " ShaderNodeBevel " ) :
if hasattr ( bpy . types , " ShaderNodeBevel " ) :
# Has newer bevel shader (2.7 nightly build series)
modes [ ' bevel_mask ' ] = ub . BakeMode ( ' bake_bevel_mask ' , type = ' EMIT ' , color = ( 0 , 0 , 0 , 1 ) , params = [ " bake_bevel_samples " , " bake_bevel_size " ] )
modes [ ' normal_tangent_bevel ' ] = ub . BakeMode ( ' bake_bevel_normal ' , type = ' NORMAL ' , color = ( 0.5 , 0.5 , 1 , 1 ) , params = [ " bake_bevel_samples " , " bake_bevel_size " ] )
modes [ ' normal_object_bevel ' ] = ub . BakeMode ( ' bake_bevel_normal ' , type = ' NORMAL ' , color = ( 0.5 , 0.5 , 1 , 1 ) , normal_space = ' OBJECT ' , params = [ " bake_bevel_samples " , " bake_bevel_size " ] )
modes [ ' bevel_mask ' ] = ub . BakeMode ( ' bake_bevel_mask ' , type = ' EMIT ' , color = (
0 , 0 , 0 , 1 ) , params = [ " bake_bevel_samples " , " bake_bevel_size " ] )
modes [ ' normal_tangent_bevel ' ] = ub . BakeMode ( ' bake_bevel_normal ' , type = ' NORMAL ' , color = (
0.5 , 0.5 , 1 , 1 ) , params = [ " bake_bevel_samples " , " bake_bevel_size " ] )
modes [ ' normal_object_bevel ' ] = ub . BakeMode ( ' bake_bevel_normal ' , type = ' NORMAL ' , color = (
0.5 , 0.5 , 1 , 1 ) , normal_space = ' OBJECT ' , params = [ " bake_bevel_samples " , " bake_bevel_size " ] )
class op ( bpy . types . Operator ) :
@ -53,43 +55,45 @@ class op(bpy.types.Operator):
bake_mode = utilities_ui . get_bake_mode ( )
if bake_mode not in modes :
self . report ( { ' ERROR_INVALID_INPUT ' } , " Uknown mode ' {} ' only available: ' {} ' " . format ( bake_mode , " , " . join ( modes . keys ( ) ) ) )
self . report ( { ' ERROR_INVALID_INPUT ' } , " Uknown mode ' {} ' only available: ' {} ' " . format (
bake_mode , " , " . join ( modes . keys ( ) ) ) )
return
# Store Selection
selected_objects = [ obj for obj in bpy . context . selected_objects ]
active_object = bpy . context . view_layer . objects . active
selected_objects = [ obj for obj in bpy . context . selected_objects ]
active_object = bpy . context . view_layer . objects . active
ub . store_bake_settings ( )
# Render sets
bake (
self = self ,
mode = bake_mode ,
size = bpy . context . scene . texToolsSettings . size ,
bake_single = bpy . context . scene . texToolsSettings . bake_force_single ,
sampling_scale = int ( bpy . context . scene . texToolsSettings . bake_sampling ) ,
samples = bpy . context . scene . texToolsSettings . bake_samples ,
ray_distance = bpy . context . scene . texToolsSettings . bake_ray_distance
self = self ,
mode = bake_mode ,
size = bpy . context . scene . texToolsSettings . size ,
bake_single = bpy . context . scene . texToolsSettings . bake_force_single ,
sampling_scale = int (
bpy . context . scene . texToolsSettings . bake_sampling ) ,
samples = bpy . context . scene . texToolsSettings . bake_samples ,
ray_distance = bpy . context . scene . texToolsSettings . bake_ray_distance
)
# Restore selection
ub . restore_bake_settings ( )
bpy . ops . object . select_all ( action = ' DESELECT ' )
for obj in selected_objects :
obj . select_set ( state = True , view_layer = None )
obj . select_set ( state = True , view_layer = None )
if active_object :
bpy . context . view_layer . objects . active = active_object
return { ' FINISHED ' }
def bake ( self , mode , size , bake_single , sampling_scale , samples , ray_distance ) :
print ( " Bake ' {} ' " . format ( mode ) )
bpy . context . scene . render . engine = modes [ mode ] . engine #Switch render engine
# Switch render engine
bpy . context . scene . render . engine = modes [ mode ] . engine
# Disable edit mode
if bpy . context . view_layer . objects . active != None and bpy . context . object . mode != ' OBJECT ' :
@ -103,29 +107,33 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
render_width = sampling_scale * size [ 0 ]
render_height = sampling_scale * size [ 1 ]
for s in range ( 0 , len ( sets ) ) :
for s in range ( 0 , len ( sets ) ) :
set = sets [ s ]
# Get image name
name_texture = " {} _ {} " . format ( set . name , mode )
if bake_single :
name_texture = " {} _ {} " . format ( sets [ 0 ] . name , mode ) # In Single mode bake into same texture
# In Single mode bake into same texture
name_texture = " {} _ {} " . format ( sets [ 0 ] . name , mode )
path = bpy . path . abspath ( " // {} .tga " . format ( name_texture ) )
# Requires 1+ low poly objects
if len ( set . objects_low ) == 0 :
self . report ( { ' ERROR_INVALID_INPUT ' } , " No low poly object as part of the ' {} ' set " . format ( set . name ) )
self . report ( { ' ERROR_INVALID_INPUT ' } ,
" No low poly object as part of the ' {} ' set " . format ( set . name ) )
return
# Check for UV maps
for obj in set . objects_low :
if not obj . data . uv_layers or len ( obj . data . uv_layers ) == 0 :
self . report ( { ' ERROR_INVALID_INPUT ' } , " No UV map available for ' {} ' " . format ( obj . name ) )
self . report ( { ' ERROR_INVALID_INPUT ' } ,
" No UV map available for ' {} ' " . format ( obj . name ) )
return
# Check for cage inconsistencies
if len ( set . objects_cage ) > 0 and ( len ( set . objects_low ) != len ( set . objects_cage ) ) :
self . report ( { ' ERROR_INVALID_INPUT ' } , " {} x cage objects do not match {} x low poly objects for ' {} ' " . format ( len ( set . objects_cage ) , len ( set . objects_low ) , obj . name ) )
self . report ( { ' ERROR_INVALID_INPUT ' } , " {} x cage objects do not match {} x low poly objects for ' {} ' " . format (
len ( set . objects_cage ) , len ( set . objects_low ) , obj . name ) )
return
# Get Materials
@ -136,7 +144,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
else :
material_empty = bpy . data . materials . new ( name = " TT_bake_node " )
# Assign Materials to Objects
if ( len ( set . objects_high ) + len ( set . objects_float ) ) == 0 :
# Low poly bake: Assign material to lowpoly
@ -153,14 +160,13 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
assign_vertex_color ( mode , obj )
assign_material ( mode , obj , material_loaded )
# Setup Image
is_clear = ( not bake_single ) or ( bake_single and s == 0 )
image = setup_image ( mode , name_texture , render_width , render_height , path , is_clear )
is_clear = ( not bake_single ) or ( bake_single and s == 0 )
image = setup_image ( mode , name_texture , render_width ,
render_height , path , is_clear )
# Assign bake node to Material
setup_image_bake_node ( set . objects_low [ 0 ] , image )
print ( " Bake ' {} ' = {} " . format ( set . name , path ) )
@ -171,13 +177,14 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
# Bake each low poly object in this set
for i in range ( len ( set . objects_low ) ) :
obj_low = set . objects_low [ i ]
obj_cage = None if i > = len ( set . objects_cage ) else set . objects_cage [ i ]
obj_cage = None if i > = len (
set . objects_cage ) else set . objects_cage [ i ]
# Disable hide render
obj_low . hide_render = False
bpy . ops . object . select_all ( action = ' DESELECT ' )
obj_low . select_set ( state = True , view_layer = None )
obj_low . select_set ( state = True , view_layer = None )
bpy . context . view_layer . objects . active = obj_low
if modes [ mode ] . engine == ' BLENDER_EEVEE ' :
@ -190,34 +197,33 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
area . spaces [ 0 ] . image = image
# bpy.data.screens['UV Editing'].areas[1].spaces[0].image = image
bpy . ops . object . mode_set ( mode = ' OBJECT ' )
for obj_high in ( set . objects_high ) :
obj_high . select_set ( state = True , view_layer = None )
obj_high . select_set ( state = True , view_layer = None )
cycles_bake (
mode ,
mode ,
bpy . context . scene . texToolsSettings . padding ,
sampling_scale ,
samples ,
sampling_scale ,
samples ,
ray_distance ,
len ( set . objects_high ) > 0 ,
obj_cage
len ( set . objects_high ) > 0 ,
obj_cage
)
# Bake Floaters seperate bake
if len ( set . objects_float ) > 0 :
bpy . ops . object . select_all ( action = ' DESELECT ' )
for obj_high in ( set . objects_float ) :
obj_high . select_set ( state = True , view_layer = None )
obj_low . select_set ( state = True , view_layer = None )
obj_high . select_set ( state = True , view_layer = None )
obj_low . select_set ( state = True , view_layer = None )
cycles_bake (
mode ,
mode ,
0 ,
sampling_scale ,
samples ,
ray_distance ,
sampling_scale ,
samples ,
ray_distance ,
len ( set . objects_float ) > 0 ,
obj_cage
)
@ -231,16 +237,16 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
for obj_cage in set . objects_cage :
obj_cage . hide_render = False
# Downsample image?
if not bake_single or ( bake_single and s == len ( sets ) - 1 ) :
# When baking single, only downsample on last bake
if render_width != size [ 0 ] or render_height != size [ 1 ] :
image . scale ( size [ 0 ] , size [ 1 ] )
image . scale ( size [ 0 ] , size [ 1 ] )
# Apply composite nodes on final image result
if modes [ mode ] . composite :
apply_composite ( image , modes [ mode ] . composite , bpy . context . scene . texToolsSettings . bake_curvature_size )
apply_composite (
image , modes [ mode ] . composite , bpy . context . scene . texToolsSettings . bake_curvature_size )
# image.save()
@ -248,8 +254,6 @@ def bake(self, mode, size, bake_single, sampling_scale, samples, ray_distance):
ub . restore_materials ( )
def apply_composite ( image , scene_name , size ) :
previous_scene = bpy . context . window . scene
@ -258,31 +262,33 @@ def apply_composite(image, scene_name, size):
if scene_name in bpy . data . scenes :
scene = bpy . data . scenes [ scene_name ]
else :
path = os . path . join ( os . path . dirname ( __file__ ) , " resources/compositing.blend " ) + " \\ Scene \\ "
bpy . ops . wm . append ( filename = scene_name , directory = path , link = False , autoselect = False )
path = os . path . join ( os . path . dirname ( __file__ ) ,
" resources/compositing.blend " ) + " \\ Scene \\ "
bpy . ops . wm . append ( filename = scene_name , directory = path ,
link = False , autoselect = False )
scene = bpy . data . scenes [ scene_name ]
if scene :
# Switch scene
bpy . context . window . scene = scene
# Setup composite nodes for Curvature
# Setup composite nodes for Curvature
if " Image " in scene . node_tree . nodes :
scene . node_tree . nodes [ " Image " ] . image = image
if " Offset " in scene . node_tree . nodes :
scene . node_tree . nodes [ " Offset " ] . outputs [ 0 ] . default_value = size
print ( " Assign offset: {} " . format ( scene . node_tree . nodes [ " Offset " ] . outputs [ 0 ] . default_value ) )
print ( " Assign offset: {} " . format (
scene . node_tree . nodes [ " Offset " ] . outputs [ 0 ] . default_value ) )
# Render image
bpy . ops . render . render ( use_viewport = False )
# Get last images of viewer node and render result
image_viewer_node = get_last_item ( " Viewer Node " , bpy . data . images )
image_render_result = get_last_item ( " Render Result " , bpy . data . images )
# Copy pixels
# Copy pixels
image . pixels = image_viewer_node . pixels [ : ]
image . update ( )
@ -291,14 +297,13 @@ def apply_composite(image, scene_name, size):
if image_render_result :
bpy . data . images . remove ( image_render_result )
# Restore scene & remove other scene
# Restore scene & remove other scene
bpy . context . window . scene = previous_scene
# Delete compositing scene
bpy . data . scenes . remove ( scene )
def get_last_item ( key_name , collection ) :
# bpy.data.images
# Get last image of a series, e.g. .001, .002, 003
@ -307,7 +312,7 @@ def get_last_item(key_name, collection):
if key_name in item . name :
keys . append ( item . name )
print ( " Search for {} x : ' {} ' " . format ( len ( keys ) , " , " . join ( keys ) ) )
print ( " Search for {} x : ' {} ' " . format ( len ( keys ) , " , " . join ( keys ) ) )
if len ( keys ) > 0 :
return collection [ keys [ - 1 ] ]
@ -315,8 +320,6 @@ def get_last_item(key_name, collection):
return None
def setup_image ( mode , name , width , height , path , is_clear ) :
image = None
@ -331,7 +334,6 @@ def setup_image(mode, name, width, height, path, is_clear):
# bpy.data.images[name].update()
# if bpy.data.images[name].has_data == False:
# Previous image does not have data, remove first
# print("Image pointer exists but no data "+name)
@ -343,13 +345,13 @@ def setup_image(mode, name, width, height, path, is_clear):
if name not in bpy . data . images :
# Create new image with 32 bit float
is_float_32 = bpy . context . preferences . addons [ " textools " ] . preferences . bake_32bit_float == ' 32 '
image = bpy . data . images . new ( name , width = width , height = height , float_buffer = is_float_32 )
image = bpy . data . images . new (
name , width = width , height = height , float_buffer = is_float_32 )
if " _normal_ " in image . name :
image . colorspace_settings . name = ' Non-Color '
image . colorspace_settings . name = ' Non-Color '
else :
image . colorspace_settings . name = ' sRGB '
else :
# Reuse existing Image
image = bpy . data . images [ name ]
@ -364,7 +366,6 @@ def setup_image(mode, name, width, height, path, is_clear):
image . generated_color = modes [ mode ] . color
image . generated_type = ' BLANK '
image . file_format = ' TARGA '
# TODO: Verify that the path exists
@ -373,11 +374,10 @@ def setup_image(mode, name, width, height, path, is_clear):
return image
def setup_image_bake_node ( obj , image ) :
if len ( obj . data . materials ) < = 0 :
print ( " ERROR, need spare material to setup active image texture to bake!!! " )
print ( " ERROR, need spare material to setup active image texture to bake!!! " )
else :
for slot in obj . material_slots :
if slot . material :
@ -397,46 +397,45 @@ def setup_image_bake_node(obj, image):
tree . nodes . active = node
def assign_vertex_color ( mode , obj ) :
if modes [ mode ] . setVColor :
modes [ mode ] . setVColor ( obj )
def assign_material ( mode , obj , material_bake = None , material_empty = None ) :
ub . store_materials ( obj )
bpy . context . view_layer . objects . active = obj
obj . select_set ( state = True , view_layer = None )
obj . select_set ( state = True , view_layer = None )
# Select All faces
bpy . ops . object . mode_set ( mode = ' EDIT ' )
bm = bmesh . from_edit_mesh ( bpy . context . active_object . data ) ;
bm = bmesh . from_edit_mesh ( bpy . context . active_object . data )
faces = [ face for face in bm . faces if face . select ]
bpy . ops . mesh . select_all ( action = ' SELECT ' )
if material_bake :
# Setup properties of bake materials
if mode == ' wireframe ' :
if " Value " in material_bake . node_tree . nodes :
material_bake . node_tree . nodes [ " Value " ] . outputs [ 0 ] . default_value = bpy . context . scene . texToolsSettings . bake_wireframe_size
material_bake . node_tree . nodes [ " Value " ] . outputs [
0 ] . default_value = bpy . context . scene . texToolsSettings . bake_wireframe_size
if mode == ' bevel_mask ' :
if " Bevel " in material_bake . node_tree . nodes :
material_bake . node_tree . nodes [ " Bevel " ] . inputs [ 0 ] . default_value = bpy . context . scene . texToolsSettings . bake_bevel_size
material_bake . node_tree . nodes [ " Bevel " ] . inputs [
0 ] . default_value = bpy . context . scene . texToolsSettings . bake_bevel_size
material_bake . node_tree . nodes [ " Bevel " ] . samples = bpy . context . scene . texToolsSettings . bake_bevel_samples
if mode == ' normal_tangent_bevel ' :
if " Bevel " in material_bake . node_tree . nodes :
material_bake . node_tree . nodes [ " Bevel " ] . inputs [ 0 ] . default_value = bpy . context . scene . texToolsSettings . bake_bevel_size
material_bake . node_tree . nodes [ " Bevel " ] . inputs [
0 ] . default_value = bpy . context . scene . texToolsSettings . bake_bevel_size
material_bake . node_tree . nodes [ " Bevel " ] . samples = bpy . context . scene . texToolsSettings . bake_bevel_samples
if mode == ' normal_object_bevel ' :
if " Bevel " in material_bake . node_tree . nodes :
material_bake . node_tree . nodes [ " Bevel " ] . inputs [ 0 ] . default_value = bpy . context . scene . texToolsSettings . bake_bevel_size
material_bake . node_tree . nodes [ " Bevel " ] . inputs [
0 ] . default_value = bpy . context . scene . texToolsSettings . bake_bevel_size
material_bake . node_tree . nodes [ " Bevel " ] . samples = bpy . context . scene . texToolsSettings . bake_bevel_samples
# Don't apply in diffuse mode
if mode != ' diffuse ' :
if material_bake :
@ -450,11 +449,11 @@ def assign_material(mode, obj, material_bake=None, material_empty=None):
bpy . ops . object . material_slot_assign ( )
elif material_empty :
# Assign material_empty if no material available
# Assign material_empty if no material available
if len ( obj . material_slots ) == 0 :
obj . data . materials . append ( material_empty )
else : # not obj.material_slots[0].material:
else : # not obj.material_slots[0].material:
obj . material_slots [ 0 ] . material = material_empty
obj . active_material_index = 0
bpy . ops . object . material_slot_assign ( )
@ -466,40 +465,33 @@ def assign_material(mode, obj, material_bake=None, material_empty=None):
bpy . ops . object . mode_set ( mode = ' OBJECT ' )
def get_material ( mode ) :
if modes [ mode ] . material == " " :
return None # No material setup requires
return None # No material setup requires
# Find or load material
name = modes [ mode ] . material
path = os . path . join ( os . path . dirname ( __file__ ) , " resources/materials.blend " ) + " \\ Material \\ "
path = os . path . join ( os . path . dirname ( __file__ ) ,
" resources/materials.blend " ) + " \\ Material \\ "
if " bevel " in mode :
path = os . path . join ( os . path . dirname ( __file__ ) , " resources/materials_2.80.blend " ) + " \\ Material \\ "
path = os . path . join ( os . path . dirname ( __file__ ) ,
" resources/materials_2.80.blend " ) + " \\ Material \\ "
print ( " Get mat {} \n {} " . format ( mode , path ) )
if bpy . data . materials . get ( name ) is None :
print ( " Material not yet loaded: " + mode )
bpy . ops . wm . append ( filename = name , directory = path , link = False , autoselect = False )
bpy . ops . wm . append ( filename = name , directory = path ,
link = False , autoselect = False )
return bpy . data . materials . get ( name )
def cycles_bake ( mode , padding , sampling_scale , samples , ray_distance , is_multi , obj_cage ) :
# if modes[mode].engine == 'BLENDER_EEVEE':
# if modes[mode].engine == 'BLENDER_EEVEE':
# # Snippet: https://gist.github.com/AndrewRayCode/760c4634a77551827de41ed67585064b
# bpy.context.scene.render.bake_margin = padding
@ -518,11 +510,10 @@ def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi,
# bpy.ops.object.bake_image()
if modes [ mode ] . engine == ' CYCLES ' or modes [ mode ] . engine == ' BLENDER_EEVEE ' :
if modes [ mode ] . engine == ' CYCLES ' or modes [ mode ] . engine == ' BLENDER_EEVEE ' :
if modes [ mode ] . normal_space == ' OBJECT ' :
# See: https://twitter.com/Linko_3D/status/963066705584054272
# See: https://twitter.com/Linko_3D/status/963066705584054272
bpy . context . scene . render . bake . normal_r = ' POS_X '
bpy . context . scene . render . bake . normal_g = ' POS_Z '
bpy . context . scene . render . bake . normal_b = ' NEG_Y '
@ -556,26 +547,37 @@ def cycles_bake(mode, padding, sampling_scale, samples, ray_distance, is_multi,
if obj_cage is None :
# Bake with Cage
bpy . ops . object . bake (
type = modes [ mode ] . type ,
use_clear = False ,
cage_extrusion = ray_distance ,
type = modes [ mode ] . type ,
use_clear = False ,
cage_extrusion = ray_distance ,
use_selected_to_active = is_multi ,
use_selected_to_active = is_multi ,
normal_space = modes [ mode ] . normal_space
)
else :
# Bake without Cage
bpy . ops . object . bake (
type = modes [ mode ] . type ,
use_clear = False ,
cage_extrusion = ray_distance ,
type = modes [ mode ] . type ,
use_clear = False ,
cage_extrusion = ray_distance ,
use_selected_to_active = is_multi ,
use_selected_to_active = is_multi ,
normal_space = modes [ mode ] . normal_space ,
# Use Cage and assign object
use_cage = True ,
# Use Cage and assign object
use_cage = True ,
cage_object = obj_cage . name
)
bpy . utils . register_class ( op )
use_selected_to_active = is_multi ,
normal_space = modes [ mode ] . normal_space ,
# Use Cage and assign object
use_cage = True ,
cage_object = obj_cage . name
)
bpy . utils . register_class ( op )