bpy.types

Access to internal Blender types

class Object:
cm3d2_bone_morph: CM3D2 Converter.misc_DATA_PT_context_arm.CNV_PG_cm3d2_bone_morph
cm3d2_bone_morph
cm3d2_menu: CM3D2 Converter.menu_file.OBJECT_PG_CM3D2Menu
cm3d2_menu
cm3d2_wide_slider: CM3D2 Converter.misc_DATA_PT_context_arm.CNV_PG_cm3d2_wide_slider
cm3d2_wide_slider
@compat.BlRegister()
@CM3D2MenuCommand('アタッチポイントの設定', name='{command_name} : {point_name}')
class CM3D2MENU_PG_AttachPointCommand(bpy.types.PropertyGroup):
220@compat.BlRegister()
221@CM3D2MenuCommand('アタッチポイントの設定', name="{command_name} : {point_name}")
222class CM3D2MENU_PG_AttachPointCommand(bpy.types.PropertyGroup):
223    bl_idname = 'CM3D2MenuAttachPointCommand'
224    '''
225    アタッチポイントの設定
226      ├ point_name(呼び出し名)
227      ├ location.x(座標)
228      ├ location.y(座標)
229      ├ location.z(座標)
230      ├ rotation.x(軸回転角度)[範囲:0±180°]
231      ├ rotation.y(軸回転角度)[範囲:0±180°]
232      └ rotation.z(軸回転角度)[範囲:0±180°]
233    '''
234    point_name = bpy.props.StringProperty     (name="Point Name", default="Attach Point", description="Name of the slot to define the attatchment point for" , options=PROP_OPTS)
235    location   = bpy.props.FloatVectorProperty(name="Location"  , default=(0, 0, 0)     , description="Location of the attatchment relative to the base bone", options=PROP_OPTS, subtype=compat.subtype('TRANSLATION'))
236    rotation   = bpy.props.FloatVectorProperty(name="Rotation"  , default=(0, 0, 0)     , description="Rotation of the attatchment relative to the base bone", options=PROP_OPTS, subtype=compat.subtype('EULER'      ))
237
238    def parse_list(self, string_list):
239        self.command = string_list[0]
240        self.point_name  = string_list[1]
241        self.location.x = float(string_list[2])
242        self.location.y = float(string_list[3])
243        self.location.z = float(string_list[4])
244        self.rotation.x = float(string_list[5]) * math.pi/180
245        self.rotation.y = float(string_list[6]) * math.pi/180
246        self.rotation.z = float(string_list[7]) * math.pi/180
247    
248    def pack_into(self, buffer):
249        buffer = buffer + struct.pack('<B', 1 + 1 + 3 + 3)
250        buffer = common.pack_str(buffer, self.command   )
251        buffer = common.pack_str(buffer, self.point_name)
252        buffer = common.pack_str(buffer, str(self.location.x)              )
253        buffer = common.pack_str(buffer, str(self.location.y)              )
254        buffer = common.pack_str(buffer, str(self.location.z)              )
255        buffer = common.pack_str(buffer, str(self.rotation.x * 180/math.pi))
256        buffer = common.pack_str(buffer, str(self.rotation.y * 180/math.pi))
257        buffer = common.pack_str(buffer, str(self.rotation.z * 180/math.pi))
258
259        return buffer
260
261    def draw(self, context, layout):
262        layout.label(text=self.name)
263
264        col = layout.column()
265        col.alignment = 'RIGHT'
266        col.prop(self, 'command', translate=False)
267        col.label(text=self.command + "     ", translate=False)
268
269        col = layout.column()
270        col.prop(self, 'point_name')
271        col.prop(self, 'location'  )
272        col.prop(self, 'rotation'  )
273        
274        col = layout.column(align=True)
275        col.operator('cm3d2menu.align_selected_to_attach_point', icon=compat.icon('OBJECT_ORIGIN')    )
276        col.operator('cm3d2menu.align_attach_point_to_selected', icon=compat.icon('ORIENTATION_LOCAL'))
CM3D2MENU_PG_AttachPointCommand
command: CM3D2 Converter.menu_file.CM3D2MENU_PG_AttachPointCommand.command_enum = 'アタッチポイントの設定'

Command
The command of this menu file command-chunk

index: int = 0
Index
location: float = 0.0

Location
Location of the attatchment relative to the base bone

name: str = ''
point_name: str = 'Attach Point'

Point Name
Name of the slot to define the attatchment point for

rotation: float = 0.0

Rotation
Rotation of the attatchment relative to the base bone

@compat.BlRegister()
class CM3D2MENU_PG_CommandPointer(bpy.types.PropertyGroup):
111@compat.BlRegister()
112class CM3D2MENU_PG_CommandPointer(bpy.types.PropertyGroup):
113    bl_idname = 'CM3D2MenuCommandPointer'
114
115    collection_name  = bpy.props.StringProperty(options={'HIDDEN'})
116    prop_index       = bpy.props.IntProperty   (options={'HIDDEN'})
117
118    def dereference(self, data):
119        return getattr(data, self.collection_name)[self.prop_index]
CM3D2MENU_PG_CommandPointer
collection_name: str = ''
prop_index: int = 0
@compat.BlRegister()
@CM3D2MenuCommand(name='{command_name}')
class CM3D2MENU_PG_MiscCommand(bpy.types.PropertyGroup):
316@compat.BlRegister()
317@CM3D2MenuCommand(name="{command_name}")
318class CM3D2MENU_PG_MiscCommand(bpy.types.PropertyGroup):
319    bl_idname = 'CM3D2MenuMiscCommand'
320    '''
321    command
322      ├ child_0
323      ├ child_1
324      ├ child_2
325      ├ ...
326      ├ child_n-1
327      └ child_n
328    '''
329    params = bpy.props.CollectionProperty(name="Parameters", options=PROP_OPTS, type=MISCCOMMAND_PG_Param)
330    
331    active_index = bpy.props.IntProperty(options={'HIDDEN'})
332
333    search = bpy.props.BoolProperty(name="Search", default=False, description="Search for suggestions", options=PROP_OPTS)
334
335    def new_param(self):
336        new_param = self.params.add()
337        new_param.value = "newparam"
338        return new_param
339
340    def remove_param(self, index: int):
341        return self.params.remove(index)
342
343    def move_param(self, old_index, new_index):
344        return self.params.move(old_index, new_index)
345
346    def parse_list(self, string_list):
347        self.command = string_list[0]
348        for param in string_list[1:]:
349            new_param = self.params.add()
350            new_param.value = param
351            new_param.name  = param
352
353    def pack_into(self, buffer):
354        buffer = buffer + struct.pack('<B', 1 + len(self.params))
355        buffer = common.pack_str(buffer, self.command)
356        for param in self.params:
357            buffer = common.pack_str(buffer, param.value)
358        return buffer
359    
360    def draw(self, context, layout):
361        enum_info = get_command_enum_info(self.command)
362        if enum_info:
363            layout.label(text=enum_info[1], icon=enum_info[3])
364
365        row = layout.row(align=True)
366        if not compat.IS_LEGACY:
367            row.use_property_split = False
368        if self.search:
369            search_data = bpy.ops.cm3d2menu.command_add.get_rna_type().properties.get('type')
370            row.prop_search(self, 'command', search_data, 'enum_items', text="", translate=True, icon='VIEWZOOM')
371        else:
372            row.prop(self, 'command', text="")
373        row.prop(self, 'search', text='', icon='ZOOM_OUT' if self.search else 'VIEWZOOM')
374        
375        
376        row = layout.row()
377        row.template_list('UI_UL_list', 'CM3D2MENU_UL_misc_command_children',
378            self, 'params'      ,
379            self, 'active_index',
380            rows    = 3,
381            maxrows = 8,
382        )
383        sub_col = row.column(align=True)
384        sub_col.operator('cm3d2menu.param_add'   , icon='ADD'   , text="")
385        sub_col.operator('cm3d2menu.param_remove', icon='REMOVE', text="")
386        #sub_col.separator()
387        #sub_col.menu("OBJECT_MT_cm3d2_menu_context_menu", icon='DOWNARROW_HLT', text="")
388        if self.active_index < len(self.params):
389            sub_col.separator()
390            sub_col.operator("cm3d2menu.param_move", icon='TRIA_UP'  , text="").direction = 'UP'  
391            sub_col.operator("cm3d2menu.param_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
CM3D2MENU_PG_MiscCommand
active_index: int = 0
command: str = 'command'

Command
The command of this menu file command-chunk

index: int = 0
Index
name: str = ''
params: dict[str, CM3D2 Converter.menu_file.MISCCOMMAND_PG_Param]
Parameters
search: bool = False

Search
Search for suggestions

@compat.BlRegister()
@CM3D2MenuCommand('prop', name='{command_name} : {prop_name} = {value}')
class CM3D2MENU_PG_PropertyCommand(bpy.types.PropertyGroup):
279@compat.BlRegister()
280@CM3D2MenuCommand('prop', name="{command_name} : {prop_name} = {value}")
281class CM3D2MENU_PG_PropertyCommand(bpy.types.PropertyGroup):
282    bl_idname = 'CM3D2PropertyMenuCommand'
283    '''
284    prop
285      ├ prop_name
286      └ value
287    '''
288    prop_name = bpy.props.StringProperty(name="Property Name" , default="prop name", description="Name of the property to set on load" , options=PROP_OPTS)
289    value     = bpy.props.FloatProperty (name="Property Value", default=50         , description="Value of the property to set on load", options=PROP_OPTS)
290    
291    def parse_list(self, string_list):
292        self.command    = string_list[0]
293        self.prop_name  = string_list[1]
294        self.value      = float(string_list[2])
295    
296    def pack_into(self, buffer):
297        buffer = buffer + struct.pack('<B', 1 + 1 + 1)
298        buffer = common.pack_str(buffer, self.command    )
299        buffer = common.pack_str(buffer, self.prop_name  )
300        buffer = common.pack_str(buffer, str(self.value) )
301
302        return buffer
303
304    def draw(self, context, layout):
305        col = layout.column()
306        col.alignment = 'RIGHT'
307        col.prop(self, 'command', translate=False)
308        col.label(text=self.command + "     ", translate=False)
309
310        col = layout.column()
311        col.label(text=self.command, translate=False)
312        col.prop(self, 'prop_name')
313        col.prop(self, 'value'    )
CM3D2MENU_PG_PropertyCommand
command: CM3D2 Converter.menu_file.CM3D2MENU_PG_PropertyCommand.command_enum = 'prop'

Command
The command of this menu file command-chunk

index: int = 0
Index
name: str = ''
prop_name: str = 'prop name'

Property Name
Name of the property to set on load

value: float = 50.0

Property Value
Value of the property to set on load

@compat.BlRegister()
class CM3D2MENU_UL_command_list(bpy.types.UIList):
12@compat.BlRegister()
13class CM3D2MENU_UL_command_list(bpy.types.UIList):
14    bl_idname      = 'CM3D2MENU_UL_command_list'
15    bl_options     = {'DEFAULT_CLOSED'}
16    bl_region_type = 'WINDOW'
17    bl_space_type  = 'PROPERTIES'
18    # The draw_item function is called for each item of the collection that is visible in the list.
19    #   data is the RNA object containing the collection,
20    #   item is the current drawn item of the collection,
21    #   icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
22    #   have custom icons ID, which are not available as enum items).
23    #   active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
24    #   active item of the collection).
25    #   active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
26    #   index is index of the current item in the collection.
27    #   flt_flag is the result of the filtering process for this item.
28    #   Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
29    #         need them.
30    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
31        command_prop = item.dereference(data)
32        # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
33        if self.layout_type in {'DEFAULT', 'COMPACT'}:
34            # You should always start your row layout by a label (icon + text), or a non-embossed text field,
35            # this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
36            # We use icon_value of label, as our given icon is an integer value, not an enum ID.
37            # Note "data" names should never be translated!
38            if command_prop:
39                command_enum_info = menu_file.get_command_enum_info(command_prop.command)
40                icon = 'NONE'
41                if command_enum_info:
42                    icon = compat.icon(command_enum_info[3])
43                layout.label(text=command_prop.name, icon=icon)
44            else:
45                layout.label(text="", translate=False, icon_value=icon)
46        # 'GRID' layout type should be as compact as possible (typically a single icon!).
47        elif self.layout_type in {'GRID'}:
48            layout.alignment = 'CENTER'
49            layout.label(text="", icon_value=icon)
CM3D2MENU_UL_command_list
@compat.BlRegister()
class CNV_PG_cm3d2_bone_morph(bpy.types.PropertyGroup):
 713@compat.BlRegister()
 714class CNV_PG_cm3d2_bone_morph(bpy.types.PropertyGroup):
 715    bl_idname = 'CNV_PG_cm3d2_bone_morph'
 716
 717    scale = bpy.props.FloatProperty(name="Scale", default=5, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="The amount by which the mesh is scaled when imported. Recommended that you use the same when at the time of export.")
 718
 719    def __calcMeasurements(self, context):                                               
 720        num    =  1340                         +                   self.sintyou * 4    +      self.DouPer * (1 + self.sintyou * 0.005) + self.KubiScl * 0.5 + self.HeadY * 0.5
 721        num2   =    55 * self.RegFat           + 50              * self.sintyou * 0.5  + 50 * self.DouPer * 0.4
 722        num3   =    55 * self.RegMeet          + 50              * self.sintyou * 0.5  + 50 * self.DouPer * 0.4
 723        num4   =    10 * self.UdeScl   * 0.1                                                             
 724        num5   =     5 * self.ArmL             +  5              * self.sintyou * 1    +  5 * self.UdeScl * 0.5
 725        num6   =    70 * self.Hara             + 50              * self.sintyou * 0.7  + 50 * self.Hara   * self.west * 0.005
 726        num7   =    10 * self.MuneL    * 2                                                                           
 727        num8   =  num7 * self.MuneTare * 0.005                                                                       
 728        num9   =    20 * self.west     * 0.5   + 15 * self.west  * self.sintyou * 0.02 + 15 * self.DouPer * self.west * 0.01
 729        num10  =    10 * self.koshi            +  7 * self.koshi * self.sintyou * 0.04
 730        num11  =     4 * self.kata                        
 731        num13  =    70                         +      self.MuneL                * 0.31 +                    self.west * 0.02      
 732        
 733        num13 -=     5 * (self.MuneS / 100)                
 734        num12  = 38000 + num2 + num3 + num4 + num5 + num6 + num7 + num8 + num9 + num10 + num11
 735        
 736        self.private_height = num   /   10
 737        self.private_weight = num12 / 1000
 738        self.private_bust   = num13
 739        self.private_waist  = 40 + self.west  * 0.25 + self.Hara   * 0.35
 740        self.private_hip    = 65 + self.koshi * 0.3  + self.RegFat * 0.025 + self.RegMeet * 0.025
 741             
 742        if   num13 <   80:
 743            self.private_cup = "A"
 744        elif num13 >= 110:
 745            self.private_cup = "N"
 746        else:
 747            cup_sizes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M']
 748            self.private_cup = cup_sizes[int((num13 - 80) / 2.5)]
 749
 750        self.private_height = int(self.private_height) * 0.01 / context.scene.unit_settings.scale_length
 751        self.private_weight = int(self.private_weight)        / context.scene.unit_settings.scale_length ** 3
 752        self.private_bust   = int(self.private_bust  ) * 0.01 / context.scene.unit_settings.scale_length
 753        self.private_waist  = int(self.private_waist ) * 0.01 / context.scene.unit_settings.scale_length
 754        self.private_hip    = int(self.private_hip   ) * 0.01 / context.scene.unit_settings.scale_length
 755        self.private_cup = "'" + self.private_cup + "'"
 756
 757        return None
 758
 759    def __calcMune(self, context):
 760        if self.BreastSize >= 0:
 761            self.MuneL = self.BreastSize
 762            self.MuneS = 0
 763        else:
 764            self.MuneL = 0
 765            self.MuneS = (self.BreastSize / -30) * 100
 766        self.__calcMuneTare(context)
 767        return None
 768
 769    def __calcMuneTare(self, context):
 770        if self.MuneTare > self.MuneL:
 771            self.MuneTare = self.MuneL
 772        else:
 773            self.__calcMeasurements(context)
 774
 775    HeadX      = bpy.props.FloatProperty(name="HeadX"     , description="Size of face (left to right)", default=  50, min=   0, max= 100, step=100, precision=0)
 776    HeadY      = bpy.props.FloatProperty(name="HeadY"     , description="Size of face (up and down)"  , default=  50, min=   0, max= 100, step=100, precision=0, update=__calcMeasurements)
 777    DouPer     = bpy.props.FloatProperty(name="DouPer"    , description="Leg length"                  , default=  50, min=-100, max= 500, step=100, precision=0, update=__calcMeasurements)
 778    sintyou    = bpy.props.FloatProperty(name="sintyou"   , description="Height"                      , default=  50, min=-300, max= 100, step=100, precision=0, update=__calcMeasurements)
 779    BreastSize = bpy.props.FloatProperty(name="BreastSize", description="Breast size"                 , default=  50, min= -30, max= 195, step=100, precision=0, update=__calcMune        )
 780    MuneTare   = bpy.props.FloatProperty(name="MuneTare"  , description="Breast sagging level"        , default=  50, min=   0, max= 195, step=100, precision=0, update=__calcMuneTare    )
 781    MuneUpDown = bpy.props.FloatProperty(name="MuneUpDown", description="Position of the nipple"      , default=  10, min= -50, max= 300, step=100, precision=0)
 782    MuneYori   = bpy.props.FloatProperty(name="MuneYori"  , description="Direction of breast"         , default=  40, min= -50, max= 200, step=100, precision=0)
 783    west       = bpy.props.FloatProperty(name="west"      , description="Waist"                       , default=  50, min= -30, max= 100, step=100, precision=0, update=__calcMeasurements)
 784    Hara       = bpy.props.FloatProperty(name="Hara"      , description="Belly"                       , default=  20, min=   0, max= 200, step=100, precision=0, update=__calcMeasurements)
 785    kata       = bpy.props.FloatProperty(name="kata"      , description="Shoulder width"              , default=  50, min=-400, max= 100, step=100, precision=0, update=__calcMeasurements)
 786    ArmL       = bpy.props.FloatProperty(name="ArmL"      , description="Size of arms"                , default=  20, min=   0, max= 100, step=100, precision=0, update=__calcMeasurements)
 787    UdeScl     = bpy.props.FloatProperty(name="UdeScl"    , description="Length of arms"              , default=  50, min=   0, max= 100, step=100, precision=0, update=__calcMeasurements)
 788    KubiScl    = bpy.props.FloatProperty(name="KubiScl"   , description="Length of neck"              , default=  50, min=   0, max= 200, step=100, precision=0, update=__calcMeasurements)
 789    koshi      = bpy.props.FloatProperty(name="koshi"     , description="Hip"                         , default=  50, min=-160, max= 200, step=100, precision=0, update=__calcMeasurements)
 790    RegFat     = bpy.props.FloatProperty(name="RegFat"    , description="Leg thickness"               , default=  40, min=   0, max= 100, step=100, precision=0, update=__calcMeasurements)
 791    RegMeet    = bpy.props.FloatProperty(name="RegMeet"   , description="Leg definition"              , default=  40, min=   0, max= 100, step=100, precision=0, update=__calcMeasurements)
 792    MuneL      = bpy.props.FloatProperty(name="MuneL"     , description="munel shapekey value"        , default=  50, min=   0)
 793    MuneS      = bpy.props.FloatProperty(name="MuneS"     , description="munes shapekey value"        , default=   0, min=   0)
 794
 795    def __measurementSetter(self, value):
 796        self.__calcMeasurements(bpy.context)
 797        return None
 798    
 799    def __newGetter(attr, recalc=False):
 800        def __getter(self):
 801            if recalc:
 802                self.__calcMeasurements(bpy.context)
 803            return getattr(self, attr)
 804        return __getter
 805
 806    private_height  = bpy.props.FloatProperty (name="private_height", options={'HIDDEN'})
 807    private_weight  = bpy.props.FloatProperty (name="private_weight", options={'HIDDEN'})
 808    private_bust    = bpy.props.FloatProperty (name="private_bust"  , options={'HIDDEN'})
 809    private_waist   = bpy.props.FloatProperty (name="private_waist" , options={'HIDDEN'})
 810    private_hip     = bpy.props.FloatProperty (name="private_hip"   , options={'HIDDEN'})
 811    private_cup     = bpy.props.StringProperty(name="private_cup"   , options={'HIDDEN'})
 812                                                     
 813    height   = bpy.props.FloatProperty (name="height", precision=3, unit=compat.unit('LENGTH'), set=__measurementSetter, get=__newGetter('private_height', recalc=True))
 814    weight   = bpy.props.FloatProperty (name="weight", precision=3, unit=compat.unit('MASS'  ), set=__measurementSetter, get=__newGetter('private_weight'))
 815    bust     = bpy.props.FloatProperty (name="bust"  , precision=3, unit=compat.unit('LENGTH'), set=__measurementSetter, get=__newGetter('private_bust'  ))
 816    waist    = bpy.props.FloatProperty (name="waist" , precision=3, unit=compat.unit('LENGTH'), set=__measurementSetter, get=__newGetter('private_waist' ))
 817    hip      = bpy.props.FloatProperty (name="hip"   , precision=3, unit=compat.unit('LENGTH'), set=__measurementSetter, get=__newGetter('private_hip'   ))
 818    cup      = bpy.props.StringProperty(name="cup"   ,                                          set=__measurementSetter, get=__newGetter('private_cup'   ))
 819                                                     
 820    def GetArmature(self, override=None):
 821        override = override or bpy.context.copy()
 822        ob = self.id_data
 823        override.update({
 824            'selected_objects'         : {ob},
 825            'selected_editable_objects': {ob},
 826            'editable_bones'           : {}  ,
 827            'selected_bones'           : {}  ,
 828            'selected_editable_bones'  : {}  ,
 829            'active_object'            : ob  ,
 830            'edit_object'              : ob  ,
 831        })
 832        return self.id_data, override
 833
 834        #armature = override['object']
 835        #if not armature or armature.type != 'ARMATURE':
 836        #    print("ERROR: Active object is not an armature")
 837        #    return None, None
 838        #
 839        #for area in bpy.context.screen.areas:
 840        #    #print(area,area.type)
 841        #    if area.type == 'OUTLINER':
 842        #        override.update({
 843        #            'blend_data': None,
 844        #            'area': area,
 845        #            'scene': bpy.context.scene,
 846        #            'screen': None,
 847        #            'space_data': area.spaces[0],
 848        #            'window': None,
 849        #            'window_manager': None,
 850        #            'object': armature,
 851        #            'active_object': armature,
 852        #            'edit_object': armature,
 853        #        })
 854        #        break
 855        #
 856        #if override['area'].type != 'OUTLINER':
 857        #    print("ERROR: There is no 3D View Present in the current workspace")
 858        #    return None, None
 859        #
 860        #if False:
 861        #    print("\n")
 862        #    for k,v in override.items():
 863        #        print(k)
 864        #    print("\n")
 865        #return armature, override
 866
 867    def GetPoseBone(self, boneName, flip=False, override=None):
 868        context = bpy.context
 869        
 870        side = "L" if flip else "R"
 871        armature, override = self.GetArmature()
 872        if not armature:
 873            return
 874        
 875        poseBoneList = armature.pose.bones
 876        poseBone = poseBoneList.get(boneName.replace("?",side)) or poseBoneList.get(boneName.replace("?","*")+"."+side)
 877
 878        # check if _SCL_ bone needs to be created
 879        if not poseBone and "_SCL_" in boneName:
 880            boneList = armature.data.edit_bones
 881            bpy.ops.object.mode_set(mode='EDIT')
 882            print("Make Scale Bone: "+boneName)
 883            copyBone = boneList.get(boneName.replace("_SCL_","").replace("?",side)) or boneList.get(boneName.replace("_SCL_","").replace("?","*")+"."+side)
 884            if copyBone:
 885                #bpy.ops.armature.select_all(override, action='DESELECT')
 886                #for v in context.selected_bones:
 887                #    v.select = False
 888                #    v.select_head = False
 889                #    v.select_tail = False
 890                #copyBone.select = True
 891                #copyBone.select_head = True
 892                #copyBone.select_tail = True
 893                #boneList.active = copyBone
 894                #bpy.ops.armature.duplicate(override)
 895                new_name = copyBone.basename+"_SCL_" + ("."+side if ("."+side) in copyBone.name else "")
 896                bone = armature.data.edit_bones.new(new_name)
 897                bone.parent = copyBone
 898                bone.head = copyBone.head
 899                bone.tail = copyBone.tail
 900                bone.roll = copyBone.roll
 901                bone.show_wire = True
 902                bone.use_deform = True
 903                copyBone['cm3d2_scl_bone'] = False
 904                bone['cm3d2_scl_bone'] = False
 905                
 906                # rename vertex groups
 907                for child in armature.children:
 908                    if child.type == 'MESH':
 909                        vertexGroup = child.vertex_groups.get(copyBone.name)
 910                        if vertexGroup:
 911                            vertexGroup.name = bone.name
 912        
 913        bpy.ops.object.mode_set(mode='POSE')
 914        poseBone = poseBone or poseBoneList.get(boneName.replace("?", side)) or poseBoneList.get(boneName.replace("?","*")+"."+side)
 915        
 916        if not poseBone:
 917            print("WARNING: Could not find bone \""+boneName+"\"")
 918            return
 919
 920        return poseBone
 921
 922    def GetDrivers(self, data_path, prop):
 923        id_data = self.id_data
 924        drivers = [None, None, None]
 925        if id_data and id_data.animation_data:
 926            for f in id_data.animation_data.drivers:
 927                fName = f.data_path
 928                #print("check",fName, "for", '["%s"].%s' % (data_path, prop))
 929                if '["{path}"].{attr}'.format(path=data_path, attr=prop) in fName:
 930                    #print("VALID!")
 931                    drivers[f.array_index] = f.driver
 932        return drivers
 933
 934
 935    def AddPositionDriver(self, prop, bone, drivers, axis, value, default=50):
 936        value = value-1
 937        if value == 0:
 938            return
 939        
 940        driver = drivers[axis]
 941        prefix = " + "
 942        
 943        if not driver:
 944            driver = bone.driver_add("location", axis).driver
 945            driver.type = 'SCRIPTED'
 946            
 947            parent_length_var = driver.variables.new()
 948            parent_length_var.type = 'SINGLE_PROP'
 949            parent_length_var.name = "parent_length"
 950            
 951            driver_target = parent_length_var.targets[0]
 952            driver_target.id_type = 'ARMATURE'
 953            driver_target.id = bone.parent.bone.id_data if not compat.IS_LEGACY else bone.parent.bone.id_data.data
 954            driver_target.data_path = bone.parent.bone.path_from_id("length")
 955
 956            head_var = driver.variables.new()
 957            head_var.type = 'SINGLE_PROP'
 958            head_var.name = "head"
 959
 960            driver_target = head_var.targets[0]
 961            driver_target.id_type = 'OBJECT'
 962            driver_target.id = bone.id_data
 963            driver_target.data_path = bone.path_from_id("head") + f"[{axis}]"
 964            
 965            if axis == 1: # if y axis, include parent bone's length, because head coords are based on parent's tail
 966                driver.expression = "(parent_length+head)_"
 967            else:
 968                driver.expression = "head_"
 969            prefix = " * ("
 970            
 971            #driver.expression = "-{direction} + {direction}", direction=rest_value
 972
 973        driver_var = driver.variables.get(prop)
 974        if not driver_var:
 975            driver_var = driver.variables.new()
 976            driver_var.type = 'SINGLE_PROP'
 977            driver_var.name = prop
 978
 979            driver_target = driver_var.targets[0]
 980            driver_target.id_type = 'OBJECT'
 981            driver_target.id = bone.id_data
 982            driver_target.data_path = bone.id_data.cm3d2_bone_morph.path_from_id(prop)
 983        
 984        # if prop isn't already a factor
 985        if not prop in driver.expression:
 986            driver.expression = driver.expression[:-1] + prefix + f"({prop}-{default})*{value/(100-default)})"
 987            
 988        return
 989
 990    def AddScaleDriver(self, prop, bone, drivers, axis, value, default=50):
 991        value = value-1
 992        if value == 0:
 993            return
 994        
 995        driver = drivers[axis]
 996        
 997        # if just created
 998        if not driver:
 999            driver = bone.driver_add("scale", axis).driver
1000            driver.type = 'SCRIPTED'
1001            driver.expression = "(1)"
1002
1003        driver_var = driver.variables.get(prop) 
1004        if not driver_var:
1005            driver_var = driver.variables.new()
1006            driver_var.type = 'SINGLE_PROP'
1007            driver_var.name = prop
1008
1009            driver_target = driver_var.targets[0]
1010            driver_target.id_type = 'OBJECT'
1011            driver_target.id = bone.id_data
1012            driver_target.data_path = bone.id_data.cm3d2_bone_morph.path_from_id(prop)
1013        
1014        # if prop isn't already a factor
1015        if not prop in driver.expression:
1016            driver.expression = driver.expression[:-1] + f" + ({prop}-{default})*{value/(100-default)})"
1017 
1018        return
1019
1020    def SetPosition(self, prop, boneName, x, y, z, default=50):
1021        # Check if object has this property
1022        #if not bpy.context.object.get(prop) or ONLY_FIX_SETTINGS:
1023        #    if ONLY_FIX_SETTINGS:
1024        #        return
1025        #x = (1-x)+1
1026        #y = (1-y)+1
1027        #z = (1-z)+1
1028
1029        #loc.x, loc.y, loc.z = loc.z, -loc.x, loc.y
1030        #x, y, z = z, x, y
1031
1032        vec = mathutils.Vector((x, y, z))
1033        vec = compat.convert_cm_to_bl_slider_space(vec)
1034
1035        pose_bone = self.GetPoseBone(boneName)
1036        if pose_bone:                                                       
1037            #rest_pos = pose_bone.bone.head_local + (pose_bone.bone.head_local - pose_bone.bone.parent.head_local) 
1038            #rest_pos = compat.mul(pose_bone.bone.matrix_local.inverted(), rest_pos)
1039            #vec = mathutils.Vector((x, y, z))# * self.scale
1040            #if pose_bone.parent:
1041            #    vec = compat.convert_cm_to_bl_bone_space(vec)
1042            #    vec = compat.mul(pose_bone.parent.bone.matrix_local, vec)
1043            #else:
1044            #    vec = compat.convert_cm_to_bl_space(vec)
1045            #vec = compat.mul(pose_bone.bone.matrix_local.inverted(), vec)
1046
1047            pose_bone.bone.use_local_location = False
1048            drivers = self.GetDrivers(pose_bone.name,'location')
1049                                                        
1050            self.AddPositionDriver(prop, pose_bone, drivers, 0, vec[0], default=default)
1051            self.AddPositionDriver(prop, pose_bone, drivers, 1, vec[1], default=default)
1052            self.AddPositionDriver(prop, pose_bone, drivers, 2, vec[2], default=default)
1053                                                                   
1054        # repeat for left side
1055        if '?' in boneName:
1056            pose_bone = self.GetPoseBone(boneName, flip=True)
1057            if pose_bone:
1058                #rest_pos = pose_bone.bone.head_local + pose_bone.bone.head
1059                #rest_pos = compat.mul(pose_bone.bone.matrix_local.inverted(), rest_pos)
1060                #vec = mathutils.Vector((x, y, z))# * self.scale
1061                #if pose_bone.parent:
1062                #    vec = compat.convert_cm_to_bl_bone_space(vec)
1063                #    vec = compat.mul(pose_bone.parent.bone.matrix_local, vec)
1064                #else:
1065                #    vec = compat.convert_cm_to_bl_space(vec)
1066                #vec = compat.mul(pose_bone.bone.matrix_local.inverted(), vec)
1067                
1068                pose_bone.bone.use_local_location = False
1069                drivers = self.GetDrivers(pose_bone.name,'location')
1070
1071                vec[2] = (1-vec[2])+1 # mirror z axis
1072                
1073                self.AddPositionDriver(prop, pose_bone, drivers, 0, vec[0], default=default)
1074                self.AddPositionDriver(prop, pose_bone, drivers, 1, vec[1], default=default)
1075                self.AddPositionDriver(prop, pose_bone, drivers, 2, vec[2], default=default)
1076                                                                            
1077        return
1078
1079    def SetScale(self, prop, boneName, x, y, z, default=50):
1080        # Check if object has this property
1081        #if not bpy.context.object.get(prop) or ONLY_FIX_SETTINGS:
1082        #    if ONLY_FIX_SETTINGS:
1083        #        return
1084
1085        #x, y, z = z, abs(-x), y
1086
1087        mat = mathutils.Matrix.Diagonal((x, y, z)).to_4x4()
1088        mat = compat.convert_cm_to_bl_bone_rotation(mat)
1089        x, y, z = -mat.to_scale()
1090
1091        bone = self.GetPoseBone(boneName)
1092        if bone:
1093            drivers = self.GetDrivers(bone.name,'scale')
1094            
1095            self.AddScaleDriver(prop, bone, drivers, 0, x, default=default)
1096            self.AddScaleDriver(prop, bone, drivers, 1, y, default=default)
1097            self.AddScaleDriver(prop, bone, drivers, 2, z, default=default)
1098        
1099        # repeat for left side
1100        if '?' in boneName:
1101            bone = self.GetPoseBone(boneName, flip=True)
1102            if bone:
1103                drivers = self.GetDrivers(bone.name,'scale')
1104                
1105                self.AddScaleDriver(prop, bone, drivers, 0, x, default=default)
1106                self.AddScaleDriver(prop, bone, drivers, 1, y, default=default)
1107                self.AddScaleDriver(prop, bone, drivers, 2, z, default=default)
1108        
1109        return
CNV_PG_cm3d2_bone_morph
ArmL: float = 20.0

ArmL
Size of arms

BreastSize: float = 50.0

BreastSize
Breast size

DouPer: float = 50.0

DouPer
Leg length

Hara: float = 20.0

Hara
Belly

HeadX: float = 50.0

HeadX
Size of face (left to right)

HeadY: float = 50.0

HeadY
Size of face (up and down)

KubiScl: float = 50.0

KubiScl
Length of neck

MuneL: float = 50.0

MuneL
munel shapekey value

MuneS: float = 0.0

MuneS
munes shapekey value

MuneTare: float = 50.0

MuneTare
Breast sagging level

MuneUpDown: float = 10.0

MuneUpDown
Position of the nipple

MuneYori: float = 40.0

MuneYori
Direction of breast

RegFat: float = 40.0

RegFat
Leg thickness

RegMeet: float = 40.0

RegMeet
Leg definition

UdeScl: float = 50.0

UdeScl
Length of arms

bust: float = 0.0
bust
cup: str = ''
cup
height: float = 0.0
height
hip: float = 0.0
hip
kata: float = 50.0

kata
Shoulder width

koshi: float = 50.0

koshi
Hip

private_bust: float = 0.0
private_cup: str = ''
private_height: float = 0.0
private_hip: float = 0.0
private_waist: float = 0.0
private_weight: float = 0.0
scale: float = 5.0

Scale
The amount by which the mesh is scaled when imported. Recommended that you use the same when at the time of export.

sintyou: float = 50.0

sintyou
Height

waist: float = 0.0
waist
weight: float = 0.0
weight
west: float = 50.0

west
Waist

@compat.BlRegister()
class CNV_PG_cm3d2_wide_slider(bpy.types.PropertyGroup):
1112@compat.BlRegister()
1113class CNV_PG_cm3d2_wide_slider(bpy.types.PropertyGroup):
1114    bl_idname = 'CNV_PG_cm3d2_wide_slider'
1115
1116    scale = bpy.props.FloatProperty(name="Scale", default=5, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="The amount by which the mesh is scaled when imported. Recommended that you use the same when at the time of export.")
1117
1118    empty  = bpy.props.EnumProperty(items=[('EMPTY','-',"This property never has a value")], name="Empty", description="This property never has a value")
1119    
1120    enable_all = bpy.props.BoolProperty(name="Enable All", description="Enable all sliders, even ones without a GUI in-game", default=False)
1121
1122    HIPPOS     = bpy.props.FloatVectorProperty(name="HIPPOS"    , description="Hips Position"         , default=(0,0,0), min=-100, max= 200, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1123    THIPOS     = bpy.props.FloatVectorProperty(name="THIPOS"    , description="Legs Position"         , default=(0,0,0), min=-100, max= 200, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1124    MTWPOS     = bpy.props.FloatVectorProperty(name="MTWPOS"    , description="Thigh Position"        , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1125    MMNPOS     = bpy.props.FloatVectorProperty(name="MMNPOS"    , description="Rear Thigh Position"   , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1126    THI2POS    = bpy.props.FloatVectorProperty(name="THI2POS"   , description="Knee Position"         , default=(0,0,0), min=-100, max= 200, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1127    SKTPOS     = bpy.props.FloatVectorProperty(name="SKTPOS"    , description="Skirt Position"        , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1128    SPIPOS     = bpy.props.FloatVectorProperty(name="SPIPOS"    , description="Lower Abdomen Position", default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1129    S0APOS     = bpy.props.FloatVectorProperty(name="S0APOS"    , description="Upper Abdomen Position", default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1130    S1POS      = bpy.props.FloatVectorProperty(name="S1POS"     , description="Lower Chest Position"  , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1131    S1APOS     = bpy.props.FloatVectorProperty(name="S1APOS"    , description="Upper Chest Position"  , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1132    MUNEPOS    = bpy.props.FloatVectorProperty(name="MUNEPOS"   , description="Breasts Position"      , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1133    MUNESUBPOS = bpy.props.FloatVectorProperty(name="MUNESUBPOS", description="Breasts Sub-Position"  , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1134    NECKPOS    = bpy.props.FloatVectorProperty(name="NECKPOS"   , description="Neck Position"         , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1135    CLVPOS     = bpy.props.FloatVectorProperty(name="CLVPOS"    , description="Clavicle Position"     , default=(0,0,0), min=-1.0, max= 1.0, precision=2, subtype=compat.subtype('XYZ'        ), unit='NONE')
1136                                                                                                
1137    PELSCL     = bpy.props.FloatVectorProperty(name="PELSCL"    , description="Pelvis Scale"          , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1138    HIPSCL     = bpy.props.FloatVectorProperty(name="HIPSCL"    , description="Hips Scale"            , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1139    THISCL     = bpy.props.FloatVectorProperty(name="THISCL"    , description="Legs Scale"            , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1140    MTWSCL     = bpy.props.FloatVectorProperty(name="MTWSCL"    , description="Thigh Scale"           , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1141    MMNSCL     = bpy.props.FloatVectorProperty(name="MMNSCL"    , description="Rear Thigh Scale"      , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1142    THISCL2    = bpy.props.FloatVectorProperty(name="THISCL2"   , description="Knee Scale"            , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1143    CALFSCL    = bpy.props.FloatVectorProperty(name="CALFSCL"   , description="Calf Scale"            , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1144    FOOTSCL    = bpy.props.FloatVectorProperty(name="FOOTSCL"   , description="Foot Scale"            , default=(1,1,1), min= 0.1, max= 2.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1145    SKTSCL     = bpy.props.FloatVectorProperty(name="SKTSCL"    , description="Skirt Scale"           , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1146    SPISCL     = bpy.props.FloatVectorProperty(name="SPISCL"    , description="Lower Abdomen Scale"   , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1147    S0ASCL     = bpy.props.FloatVectorProperty(name="S0ASCL"    , description="Upper Abdomen Scale"   , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1148    S1_SCL     = bpy.props.FloatVectorProperty(name="S1_SCL"    , description="Lower Chest Scale"     , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1149    S1ASCL     = bpy.props.FloatVectorProperty(name="S1ASCL"    , description="Upper Chest Scale"     , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1150    S1ABASESCL = bpy.props.FloatVectorProperty(name="S1ABASESCL", description="Upper Torso Scale"     , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1151    MUNESCL    = bpy.props.FloatVectorProperty(name="MUNESCL"   , description="Breasts Scale"         , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1152    MUNESUBSCL = bpy.props.FloatVectorProperty(name="MUNESUBSCL", description="Breasts Sub-Scale"     , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1153    NECKSCL    = bpy.props.FloatVectorProperty(name="NECKSCL"   , description="Neck Scale"            , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1154    CLVSCL     = bpy.props.FloatVectorProperty(name="CLVSCL"    , description="Clavicle Scale"        , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1155    KATASCL    = bpy.props.FloatVectorProperty(name="KATASCL"   , description="Shoulders Scale"       , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1156    UPARMSCL   = bpy.props.FloatVectorProperty(name="UPARMSCL"  , description="Upper Arm Scale"       , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1157    FARMSCL    = bpy.props.FloatVectorProperty(name="FARMSCL"   , description="Forearm Scale"         , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1158    HANDSCL    = bpy.props.FloatVectorProperty(name="HANDSCL"   , description="Hand Scale"            , default=(1,1,1), min= 0.1, max= 3.0, precision=2, subtype=compat.subtype('XYZ_LENGTH' ), unit='NONE')
1159                                                                                                                                        
1160                                                                                                                                        
1161    def GetArmature(self, override=None):
1162        return CNV_PG_cm3d2_bone_morph.GetArmature(self, override=override)
1163    
1164    def GetPoseBone(self, boneName, flip=False, override=None):
1165        return CNV_PG_cm3d2_bone_morph.GetPoseBone(self, boneName, flip=flip, override=override)
1166
1167    def GetDrivers(self, data_path, prop):
1168        return CNV_PG_cm3d2_bone_morph.GetDrivers(self, data_path, prop)
1169
1170
1171    def AddPositionDriver(self, prop, index, bone, drivers, axis, value):
1172        if value == 0:
1173            return
1174              
1175        driver = drivers[axis]
1176
1177        if not driver:
1178            driver = bone.driver_add("location",axis).driver
1179            driver.type = 'SCRIPTED'
1180            driver.expression = "0"
1181
1182        prop_var = prop + f"_{index}_"
1183        driver_var = driver.variables.get(prop_var) 
1184        if not driver_var:
1185            driver_var = driver.variables.new()
1186            driver_var.type = 'SINGLE_PROP'
1187            driver_var.name = prop_var
1188
1189            driver_target = driver_var.targets[0]
1190            driver_target.id_type = 'OBJECT'
1191            driver_target.id = bone.id_data
1192            driver_target.data_path = bone.id_data.cm3d2_wide_slider.path_from_id(prop) + f"[{index}]"
1193
1194              
1195        # if prop isn't already a factor
1196        if not prop_var in driver.expression:
1197            driver.expression = driver.expression + f" + {prop_var}*{value}"
1198                
1199        return
1200              
1201              
1202    def AddScaleDriver(self, prop, index, bone, drivers, axis):
1203        if index < 0:
1204            return
1205              
1206        driver = drivers[axis]
1207        if not driver:
1208            driver = bone.driver_add("scale", axis).driver
1209            driver.type = 'SCRIPTED'
1210            driver.expression = "1"
1211        
1212        prop_var = f"{prop}_{index}_"
1213        driver_var = driver.variables.get(prop_var) 
1214        if not driver_var:
1215            driver_var = driver.variables.new()
1216            driver_var.type = 'SINGLE_PROP'
1217            driver_var.name = prop_var
1218
1219            driver_target = driver_var.targets[0]
1220            driver_target.id_type = 'OBJECT'
1221            driver_target.id = bone.id_data
1222            driver_target.data_path = bone.id_data.cm3d2_wide_slider.path_from_id(prop) + f"[{index}]"
1223
1224              
1225        # if prop isn't already a factor
1226        if not prop_var in driver.expression:
1227            driver.expression = driver.expression + f" * {prop_var}"
1228                
1229        return
1230              
1231                      
1232    def AddVectorProperty(self, object, prop, value=None, default=0.0, min=-100, max=200):
1233        #value = value or [default, default, default]
1234        #object[prop] = not RESET_SETTINGS and object.get(prop) or value
1235        #object['_RNA_UI'][prop] = {
1236        #    "description": "",
1237        #    "default": default,
1238        #    "min": min,
1239        #    "max": max,
1240        #    "soft_min": min,
1241        #    "soft_max": max,
1242        #}     
1243        return
1244    
1245              
1246    def SetPosition(self, prop, boneName, ux, uy, uz, axisOrder=[0,1,2], axisFlip=None):
1247        # Check if object has this property
1248        #if not bpy.context.object.get(prop) or ONLY_FIX_SETTINGS:
1249        #    self.AddVectorProperty(bpy.context.object, prop)
1250        #    if ONLY_FIX_SETTINGS:
1251        #        return
1252
1253        mat = get_axis_order_matrix(axisOrder).to_4x4()
1254        mat.translation = mathutils.Vector((ux, uy, uz)) * self.scale
1255        mat = compat.convert_cm_to_bl_bone_space(mat)
1256        uVec = mat.to_translation()
1257        axisOrder = get_matrix_axis_order(mat.to_3x3())
1258
1259        if axisFlip != None:
1260            flipVec = get_axis_index_vector(axisFlip)
1261            flipVec = compat.convert_cm_to_bl_bone_space(flipVec)
1262            axisFlip = get_vector_axis_index(flipVec)
1263        
1264        ##ux, uy, uz = uz*5, ux*5, -uy*5
1265        ##axisFlip = axisOrder[axisFlip] if axisFlip else None
1266        #axisFlip = 1 if axisFlip == 0 else ( 2 if axisFlip == 1 else (0 if axisFlip == 2 else None) )
1267        ##axisFlip = axisOrder[axisFlip] if axisFlip else None
1268        #axisOrder[0], axisOrder[1], axisOrder[2] = axisOrder[2], axisOrder[0], axisOrder[1]
1269        ##axisFlip = axisOrder[axisFlip] if axisFlip != None else None
1270        
1271        bone = self.GetPoseBone(boneName)
1272        if bone:
1273            bone.bone.use_local_location = False
1274            drivers = self.GetDrivers(bone.name,'location')
1275        
1276            self.AddPositionDriver(prop, axisOrder[0], bone, drivers, 0, uVec[0])
1277            self.AddPositionDriver(prop, axisOrder[1], bone, drivers, 1, uVec[1])
1278            self.AddPositionDriver(prop, axisOrder[2], bone, drivers, 2, uVec[2])
1279        
1280        # repeat for left side
1281        if '?' in boneName:
1282            bone = self.GetPoseBone(boneName, flip=True)
1283            if bone:
1284                bone.bone.use_local_location = False
1285                drivers = self.GetDrivers(bone.name,'location')
1286
1287                if axisFlip != None:
1288                    print(axisFlip)
1289                    uVec[axisFlip] *= -1
1290                
1291                #if   axisFlip == 0:
1292                #    ux = -ux
1293                #elif axisFlip == 1:
1294                #    uy = -uy
1295                #elif axisFlip == 2:
1296                #    uz = -uz
1297                
1298                self.AddPositionDriver(prop, axisOrder[0], bone, drivers, 0, uVec[0])
1299                self.AddPositionDriver(prop, axisOrder[1], bone, drivers, 1, uVec[1])
1300                self.AddPositionDriver(prop, axisOrder[2], bone, drivers, 2, uVec[2])
1301                                                                             
1302        return
1303
1304
1305    def SetScale(self, prop, boneName, axisOrder=[0,1,2]):
1306        # Check if object has this property
1307        #if not bpy.context.object.get(prop) or ONLY_FIX_SETTINGS:
1308        #    self.AddVectorProperty(bpy.context.object, prop, default=1.0, min=0.1, max=3.0)
1309        #    if ONLY_FIX_SETTINGS:
1310        #        return
1311
1312        # x, y, z = x, z, y
1313        #axisOrder[0], axisOrder[1], axisOrder[2] = axisOrder[0], axisOrder[2], axisOrder[1]
1314        
1315        axisMat = get_axis_order_matrix(axisOrder).to_4x4()
1316        axisMat = compat.convert_cm_to_bl_bone_rotation(axisMat)
1317        #axisMat = compat.convert_cm_to_bl_bone_space(axisMat)
1318        axisOrder = get_matrix_axis_order(axisMat)
1319        
1320        bone = self.GetPoseBone(boneName)
1321        if bone:
1322            drivers = self.GetDrivers(bone.name,'scale')
1323            
1324            self.AddScaleDriver(prop, axisOrder[0], bone, drivers, 2)
1325            self.AddScaleDriver(prop, axisOrder[1], bone, drivers, 1)
1326            self.AddScaleDriver(prop, axisOrder[2], bone, drivers, 0)
1327        
1328        # repeat for left side
1329        if '?' in boneName:
1330            bone = self.GetPoseBone(boneName, True)
1331            if bone:
1332                drivers = self.GetDrivers(bone.name,'scale')
1333
1334                self.AddScaleDriver(prop, axisOrder[0], bone, drivers, 2)
1335                self.AddScaleDriver(prop, axisOrder[1], bone, drivers, 1)
1336                self.AddScaleDriver(prop, axisOrder[2], bone, drivers, 0)
1337        
1338        return
CNV_PG_cm3d2_wide_slider
CALFSCL: float = 0.0

CALFSCL
Calf Scale

CLVPOS: float = 0.0

CLVPOS
Clavicle Position

CLVSCL: float = 0.0

CLVSCL
Clavicle Scale

FARMSCL: float = 0.0

FARMSCL
Forearm Scale

FOOTSCL: float = 0.0

FOOTSCL
Foot Scale

HANDSCL: float = 0.0

HANDSCL
Hand Scale

HIPPOS: float = 0.0

HIPPOS
Hips Position

HIPSCL: float = 0.0

HIPSCL
Hips Scale

KATASCL: float = 0.0

KATASCL
Shoulders Scale

MMNPOS: float = 0.0

MMNPOS
Rear Thigh Position

MMNSCL: float = 0.0

MMNSCL
Rear Thigh Scale

MTWPOS: float = 0.0

MTWPOS
Thigh Position

MTWSCL: float = 0.0

MTWSCL
Thigh Scale

MUNEPOS: float = 0.0

MUNEPOS
Breasts Position

MUNESCL: float = 0.0

MUNESCL
Breasts Scale

MUNESUBPOS: float = 0.0

MUNESUBPOS
Breasts Sub-Position

MUNESUBSCL: float = 0.0

MUNESUBSCL
Breasts Sub-Scale

NECKPOS: float = 0.0

NECKPOS
Neck Position

NECKSCL: float = 0.0

NECKSCL
Neck Scale

PELSCL: float = 0.0

PELSCL
Pelvis Scale

S0APOS: float = 0.0

S0APOS
Upper Abdomen Position

S0ASCL: float = 0.0

S0ASCL
Upper Abdomen Scale

S1ABASESCL: float = 0.0

S1ABASESCL
Upper Torso Scale

S1APOS: float = 0.0

S1APOS
Upper Chest Position

S1ASCL: float = 0.0

S1ASCL
Upper Chest Scale

S1POS: float = 0.0

S1POS
Lower Chest Position

S1_SCL: float = 0.0

S1_SCL
Lower Chest Scale

SKTPOS: float = 0.0

SKTPOS
Skirt Position

SKTSCL: float = 0.0

SKTSCL
Skirt Scale

SPIPOS: float = 0.0

SPIPOS
Lower Abdomen Position

SPISCL: float = 0.0

SPISCL
Lower Abdomen Scale

THI2POS: float = 0.0

THI2POS
Knee Position

THIPOS: float = 0.0

THIPOS
Legs Position

THISCL: float = 0.0

THISCL
Legs Scale

THISCL2: float = 0.0

THISCL2
Knee Scale

UPARMSCL: float = 0.0

UPARMSCL
Upper Arm Scale

empty: CM3D2 Converter.misc_DATA_PT_context_arm.CNV_PG_cm3d2_wide_slider.empty_enum = 'EMPTY'

Empty
This property never has a value

enable_all: bool = False

Enable All
Enable all sliders, even ones without a GUI in-game

scale: float = 5.0

Scale
The amount by which the mesh is scaled when imported. Recommended that you use the same when at the time of export.

@compat.BlRegister()
class CNV_SelectorItem(bpy.types.PropertyGroup):
1286@compat.BlRegister()
1287class CNV_SelectorItem(bpy.types.PropertyGroup):
1288    bl_label       = "CNV_SelectorItem"
1289    bl_region_type = 'WINDOW'
1290    bl_space_type  = 'PROPERTIES'
1291
1292    name      = bpy.props.StringProperty (name="Name"    , default="Unknown")
1293    value     = bpy.props.BoolProperty   (name="Value"   , default=True     )
1294    index     = bpy.props.IntProperty    (name="Index"   , default=-1       )
1295    preferred = bpy.props.BoolProperty   (name="Prefered", default=True     )
1296    icon      = bpy.props.StringProperty (name="Icon"    , default='NONE'   )
1297
1298    filter0   = bpy.props.BoolProperty   (name="Filter 0", default=False    )
1299    filter1   = bpy.props.BoolProperty   (name="Filter 1", default=False    )
1300    filter2   = bpy.props.BoolProperty   (name="Filter 2", default=False    )
1301    filter3   = bpy.props.BoolProperty   (name="Filter 3", default=False    )
CNV_SelectorItem
filter0: bool = False
Filter 0
filter1: bool = False
Filter 1
filter2: bool = False
Filter 2
filter3: bool = False
Filter 3
icon: str = 'NONE'
Icon
index: int = -1
Index
name: str = 'Unknown'
Name
preferred: bool = True
Prefered
value: bool = True
Value
@compat.BlRegister()
class CNV_UL_modifier_selector(bpy.types.UIList):
 27@compat.BlRegister()
 28class CNV_UL_modifier_selector(common.CNV_UL_generic_selector):
 29    bl_label       = 'CNV_UL_modifier_selector'
 30    bl_options     = {'DEFAULT_CLOSED'}
 31    bl_region_type = 'WINDOW'
 32    bl_space_type  = 'PROPERTIES'
 33
 34    # Constants (flags)
 35    # Be careful not to shadow FILTER_ITEM!
 36    #bitflag_forced_true  = 1 << 0
 37    #bitflag_forced_false = 1 << 1
 38    #force_values = False
 39    #did_force_values = False
 40
 41    force_values = bpy.props.BoolProperty(
 42        name="force_values",
 43        default=False,
 44        options=set(),
 45    )
 46
 47    did_force_values = bpy.props.BoolProperty(
 48        name="force_values",
 49        default=False,
 50        options=set(),
 51    )
 52
 53    # This allows us to have mutually exclusive options, which are also all disable-able!
 54    def _gen_force_values(self, context):
 55        setattr(self, "force_values", True)
 56        setattr(self, "did_force_values", False)
 57        print("SET TRUE force_values =", self.force_values)
 58    
 59    def _gen_visible_update(name1, name2):
 60        def _u(self, context):
 61            self._gen_force_values(context)
 62            if (getattr(self, name1)):
 63                setattr(self, name2, False)
 64        return _u
 65    use_filter_viewport_visible = bpy.props.BoolProperty(
 66        name="Viewport",
 67        default=False,
 68        options=set(),
 69        description="Only enable modifiers visible in viewport",
 70        update=_gen_visible_update("use_filter_viewport_visible", "use_filter_renderer_visible"),
 71    )
 72    use_filter_renderer_visible = bpy.props.BoolProperty(
 73        name="Renderer",
 74        default=False,
 75        options=set(),
 76        description="Only enable modifiers visible in renderer",
 77        update=_gen_visible_update("use_filter_renderer_visible", "use_filter_viewport_visible"),
 78    )
 79    use_filter_reversed_visible = bpy.props.BoolProperty(
 80        name="Reverse Visible Filter",
 81        default=False,
 82        options=set(),
 83        description="Reverse the selected visible-in filter",
 84        update=_gen_force_values
 85    )
 86
 87
 88    use_filter_name_reverse = bpy.props.BoolProperty(
 89        name="Reverse Name",
 90        default=False,
 91        options=set(),
 92        description="Reverse name filtering",
 93    )
 94
 95    def _gen_order_update(name1, name2):
 96        def _u(self, ctxt):
 97            if (getattr(self, name1)):
 98                setattr(self, name2, False)
 99        return _u
100    use_order_name = bpy.props.BoolProperty(
101        name="Name", default=False, options=set(),
102        description="Sort groups by their name (case-insensitive)",
103        update=_gen_order_update("use_order_name", "use_order_importance"),
104    )
105    use_filter_orderby_invert = bpy.props.BoolProperty(
106        name="Order by Invert",
107        default=False,
108        options=set(),
109        description="Invert the sort by order"
110    )
111
112
113    def draw_filter(self, context, layout):
114        row = layout.row()
115        row.label(text="Visible in:")
116        subrow = row.row(align=True)
117        subrow.prop(self, "use_filter_viewport_visible", toggle=True)
118        subrow.prop(self, "use_filter_renderer_visible", toggle=True)
119        icon = 'ZOOM_OUT' if self.use_filter_reversed_visible else 'ZOOM_IN'
120        icon = compat.icon(icon)
121        subrow.prop(self, "use_filter_reversed_visible", text="", icon=icon)
122
123        super(CNV_UL_modifier_selector, self).draw_filter(context, layout)
124
125    def filter_items(self, context, data, propname):
126        flt_flags, flt_neworder = super(CNV_UL_modifier_selector, self).filter_items(context, data, propname)
127        items = getattr(data, propname)
128
129        if getattr(self, 'did_force_values'):
130            setattr(self,'force_values', False)
131        setattr(self, 'did_force_values', getattr(self, 'force_values'))
132
133        print("CHECK force_values = ", getattr(self, 'force_values'))
134
135        if self.use_filter_viewport_visible or self.use_filter_renderer_visible or getattr(self, 'force_values'):
136
137            if not self.use_filter_reversed_visible:
138                in_flag  = self.bitflag_forced_true 
139                out_flag = ~(self.bitflag_forced_false | self.bitflag_soft_filter)
140            else:
141                in_flag  = self.bitflag_forced_false | self.bitflag_soft_filter
142                out_flag = ~self.bitflag_forced_true
143
144            for index, item in enumerate(items):
145                if getattr(self, 'force_values'):
146                    flt_flags[index] |= self.bitflag_forced_value
147
148                if self.use_filter_viewport_visible and item.filter0:
149                    flt_flags[index] |= in_flag
150                    flt_flags[index] &= out_flag
151                elif self.use_filter_renderer_visible and item.filter1:
152                    flt_flags[index] |= in_flag
153                    flt_flags[index] &= out_flag
154                elif not self.use_filter_viewport_visible and not self.use_filter_renderer_visible:
155                    pass
156                else:
157                    flt_flags[index] |= ~out_flag
158                    flt_flags[index] &= ~in_flag
159
160        return flt_flags, flt_neworder
CNV_UL_modifier_selector
did_force_values: bool = False
force_values
force_values: bool = False
force_values
use_filter_name_reverse: bool = False

Reverse Name
Reverse name filtering

use_filter_orderby_invert: bool = False

Order by Invert
Invert the sort by order

use_filter_renderer_visible: bool = False

Renderer
Only enable modifiers visible in renderer

use_filter_reversed_visible: bool = False

Reverse Visible Filter
Reverse the selected visible-in filter

use_filter_viewport_visible: bool = False

Viewport
Only enable modifiers visible in viewport

use_order_name: bool = False

Name
Sort groups by their name (case-insensitive)

@compat.BlRegister()
class CNV_UL_vgroups_selector(bpy.types.UIList):
 851@compat.BlRegister()
 852class CNV_UL_vgroups_selector(bpy.types.UIList):
 853    bl_label = "Vertex Groups Selector"
 854    bl_options = {'DEFAULT_CLOSED'}
 855    bl_region_type = 'WINDOW'
 856    bl_space_type = 'PROPERTIES'
 857
 858    # Constants (flags)
 859    # Be careful not to shadow FILTER_ITEM!
 860    VGROUP_EMPTY  = 1 << 1
 861    VGROUP_DEFORM = 1 << 0
 862
 863    armature = None
 864    local_bone_names = None
 865    cached_values = {}
 866
 867    expanded_layout = False
 868
 869    # Custom properties, saved with .blend file.
 870    use_filter_name_reverse = bpy.props.BoolProperty(
 871        name="Reverse Name",
 872        default=False,
 873        options=set(),
 874        description="Reverse name filtering",
 875    )
 876    use_filter_deform = bpy.props.BoolProperty(
 877        name="Only Deform",
 878        default=False,
 879        options=set(),
 880        description="Only show deforming vertex groups",
 881    )
 882    use_filter_deform_reverse = bpy.props.BoolProperty(
 883        name="Other",
 884        default=False,
 885        options=set(),
 886        description="Only show non-deforming vertex groups",
 887    )
 888    use_filter_empty = bpy.props.BoolProperty(
 889        name="Filter Empty",
 890        default=False,
 891        options=set(),
 892        description="Whether to filter empty vertex groups",
 893    )
 894    use_filter_empty_reverse = bpy.props.BoolProperty(
 895        name="Reverse Empty",
 896        default=False,
 897        options=set(),
 898        description="Reverse empty filtering",
 899    )
 900
 901    # This allows us to have mutually exclusive options, which are also all disable-able!
 902    def _gen_order_update(name1, name2):
 903        def _u(self, ctxt):
 904            if (getattr(self, name1)):
 905                setattr(self, name2, False)
 906        return _u
 907    use_order_name = bpy.props.BoolProperty(
 908        name="Name", default=False, options=set(),
 909        description="Sort groups by their name (case-insensitive)",
 910        update=_gen_order_update("use_order_name", "use_order_importance"),
 911    )
 912    use_order_importance = bpy.props.BoolProperty(
 913        name="Importance",
 914        default=False,
 915        options=set(),
 916        description="Sort groups by their average weight in the mesh",
 917        update=_gen_order_update("use_order_importance", "use_order_name"),
 918    )
 919    use_filter_orderby_invert = bpy.props.BoolProperty(
 920        name="Order by Invert",
 921        default=False,
 922        options=set(),
 923        description="Invert the sort by order"
 924    )
 925
 926    # Usual draw item function.
 927    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
 928        # Just in case, we do not use it here!
 929        self.use_filter_invert = False
 930
 931        # assert(isinstance(item, bpy.types.VertexGroup)
 932        #vgroup = getattr(data, 'matched_vgroups')[item.index]
 933        if self.layout_type in {'DEFAULT', 'COMPACT'}:
 934            # Here we use one feature of new filtering feature: it can pass data to draw_item, through flt_flag
 935            # parameter, which contains exactly what filter_items set in its filter list for this item!
 936            # In this case, we show empty groups grayed out.
 937            cached_value = self.cached_values.get(item.name, None)
 938            if (cached_value != None) and (cached_value != item.value):
 939                item.preferred = item.value
 940
 941            if self.use_filter_deform:
 942                item.value = bool(flt_flag & self.VGROUP_DEFORM) and item.preferred
 943            else:
 944                item.value = item.preferred
 945
 946            self.cached_values[item.name] = item.value
 947
 948            if flt_flag & (self.VGROUP_EMPTY | self.VGROUP_DEFORM):
 949                col = layout.column()
 950                col.enabled = False
 951                col.alignment = 'LEFT'
 952                col.prop(item, "value", text=item.name, emboss=False, icon_value=icon)
 953            else:
 954                layout.prop(item, "value", text=item.name, icon_value=icon)
 955
 956            icon = 'RADIOBUT_ON' if item.preferred else 'RADIOBUT_OFF'
 957            layout.prop(item, "preferred", text="", icon=compat.icon(icon), emboss=False)
 958        elif self.layout_type in {'GRID'}:
 959            layout.alignment = 'CENTER'
 960            if flt_flag & self.VGROUP_EMPTY:
 961                layout.enabled = False
 962            layout.label(text="", icon_value=icon)
 963
 964    def draw_filter(self, context, layout):
 965        # Nothing much to say here, it's usual UI code...
 966        row = layout.row()
 967        if not self.expanded_layout:
 968            layout.active = True
 969            layout.enabled = True
 970            row.active = True
 971            row.enabled = True
 972            self.expanded_layout = True
 973
 974        subrow = row.row(align=True)
 975        subrow.prop(self, "filter_name", text="")
 976        icon = 'ZOOM_OUT' if self.use_filter_name_reverse else 'ZOOM_IN'
 977        subrow.prop(self, "use_filter_name_reverse", text="", icon=icon)
 978
 979        subrow = row.row(align=True)
 980        subrow.prop(self, "use_filter_deform", toggle=True)
 981        icon = 'ZOOM_OUT' if self.use_filter_deform_reverse else 'ZOOM_IN'
 982        subrow.prop(self, "use_filter_deform_reverse", text="", icon=icon)
 983
 984        #subrow = row.row(align=True)
 985        #subrow.prop(self, "use_filter_empty", toggle=True)
 986        #icon = 'ZOOM_OUT' if self.use_filter_empty_reverse else 'ZOOM_IN'
 987        #subrow.prop(self, "use_filter_empty_reverse", text="", icon=icon)
 988
 989        row = layout.row(align=True)
 990        row.label(text="Order by:")
 991        row.prop(self, "use_order_name", toggle=True)
 992        #row.prop(self, "use_order_importance", toggle=True)
 993        icon = 'TRIA_UP' if self.use_filter_orderby_invert else 'TRIA_DOWN'
 994        row.prop(self, "use_filter_orderby_invert", text="", icon=icon)
 995
 996    def filter_items_empty_vgroups(self, context, vgroups):
 997        # This helper function checks vgroups to find out whether they are empty, and what's their average weights.
 998        # TODO: This should be RNA helper actually (a vgroup prop like "raw_data: ((vidx, vweight), etc.)").
 999        #       Too slow for python!
1000        obj_data = context.active_object.data
1001        ret = {vg.index: [True, 0.0] for vg in vgroups}
1002        if hasattr(obj_data, "vertices"):  # Mesh data
1003            if obj_data.is_editmode:
1004                import bmesh
1005                bm = bmesh.from_edit_mesh(obj_data)
1006                # only ever one deform weight layer
1007                dvert_lay = bm.verts.layers.deform.active
1008                fact = 1 / len(bm.verts)
1009                if dvert_lay:
1010                    for v in bm.verts:
1011                        for vg_idx, vg_weight in v[dvert_lay].items():
1012                            ret[vg_idx][0] = False
1013                            ret[vg_idx][1] += vg_weight * fact
1014            else:
1015                fact = 1 / len(obj_data.vertices)
1016                for v in obj_data.vertices:
1017                    for vg in v.groups:
1018                        ret[vg.group][0] = False
1019                        ret[vg.group][1] += vg.weight * fact
1020        elif hasattr(obj_data, "points"):  # Lattice data
1021            # XXX no access to lattice editdata?
1022            fact = 1 / len(obj_data.points)
1023            for v in obj_data.points:
1024                for vg in v.groups:
1025                    ret[vg.group][0] = False
1026                    ret[vg.group][1] += vg.weight * fact
1027        return ret
1028
1029    def filter_items(self, context, data, propname):
1030        # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists:
1031        # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the
1032        #   matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the
1033        #   first one to mark VGROUP_EMPTY.
1034        # * The second one is for reordering, it must return a list containing the new indices of the items (which
1035        #   gives us a mapping org_idx -> new_idx).
1036        # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).
1037        # If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than
1038        # returning full lists doing nothing!).
1039        items = getattr(data, propname)
1040        
1041        if self.armature == None:
1042            target_ob, source_ob = common.get_target_and_source_ob(context)
1043            armature_ob = target_ob.find_armature() or source_ob.find_armature()
1044            self.armature = armature_ob and armature_ob.data or False
1045
1046        if not self.local_bone_names:
1047            target_ob, source_ob = common.get_target_and_source_ob(context)
1048            bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None
1049            if bone_data_ob:
1050                local_bone_data = model_export.CNV_OT_export_cm3d2_model.local_bone_data_parser(model_export.CNV_OT_export_cm3d2_model.indexed_data_generator(bone_data_ob, prefix="LocalBoneData:"))
1051                self.local_bone_names = [ bone['name'] for bone in local_bone_data ]
1052        
1053        if not self.cached_values:
1054            self.cached_values = { item.name: item.value for item in items }
1055        #vgroups = [ getattr(data, 'matched_vgroups')[item.index][0]   for item in items ]
1056        helper_funcs = bpy.types.UI_UL_list
1057
1058        # Default return values.
1059        flt_flags = []
1060        flt_neworder = []
1061
1062        # Pre-compute of vgroups data, CPU-intensive. :/
1063        #vgroups_empty = self.filter_items_empty_vgroups(context, vgroups)
1064
1065        # Filtering by name
1066        if self.filter_name:
1067            flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, items, "name",
1068                                                          reverse=self.use_filter_name_reverse)
1069        if not flt_flags:
1070            flt_flags = [self.bitflag_filter_item] * len(items)
1071        
1072        for idx, vg in enumerate(items):
1073            # Filter by deform.
1074            if self.use_filter_deform:
1075                flt_flags[idx] |= self.VGROUP_DEFORM
1076                if self.use_filter_deform:
1077                    if self.armature and self.armature.get(vg.name):
1078                        if not self.use_filter_deform_reverse:
1079                            flt_flags[idx] &= ~self.VGROUP_DEFORM
1080                    elif bone_data_ob and (vg.name in self.local_bone_names):
1081                        if not self.use_filter_deform_reverse:
1082                            flt_flags[idx] &= ~self.VGROUP_DEFORM
1083                    elif self.use_filter_deform_reverse or (not self.armature and not self.local_bone_names):
1084                        flt_flags[idx] &= ~self.VGROUP_DEFORM
1085            else:
1086                flt_flags[idx] &= ~self.VGROUP_DEFORM
1087
1088            # Filter by emptiness.
1089            #if vgroups_empty[vg.index][0]:
1090            #    flt_flags[idx] |= self.VGROUP_EMPTY
1091            #    if self.use_filter_empty and self.use_filter_empty_reverse:
1092            #        flt_flags[idx] &= ~self.bitflag_filter_item
1093            #elif self.use_filter_empty and not self.use_filter_empty_reverse:
1094            #    flt_flags[idx] &= ~self.bitflag_filter_item
1095
1096        # Reorder by name or average weight.
1097        if self.use_order_name:
1098            flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name")
1099        #elif self.use_order_importance:
1100        #    _sort = [(idx, vgroups_empty[vg.index][1]) for idx, vg in enumerate(vgroups)]
1101        #    flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], True)
1102
1103        return flt_flags, flt_neworder
CNV_UL_vgroups_selector
use_filter_deform: bool = False

Only Deform
Only show deforming vertex groups

use_filter_deform_reverse: bool = False

Other
Only show non-deforming vertex groups

use_filter_empty: bool = False

Filter Empty
Whether to filter empty vertex groups

use_filter_empty_reverse: bool = False

Reverse Empty
Reverse empty filtering

use_filter_name_reverse: bool = False

Reverse Name
Reverse name filtering

use_filter_orderby_invert: bool = False

Order by Invert
Invert the sort by order

use_order_importance: bool = False

Importance
Sort groups by their average weight in the mesh

use_order_name: bool = False

Name
Sort groups by their name (case-insensitive)

@compat.BlRegister()
class MISCCOMMAND_PG_Param(bpy.types.PropertyGroup):
122@compat.BlRegister()
123class MISCCOMMAND_PG_Param(bpy.types.PropertyGroup):
124    bl_idname = 'CM3D2MenuParam'
125
126    # Really the value should be saved, not the name, but template_list() doesn't like that so they're switched.
127    def _s(self, value):
128        self.name = value
129
130    #name = bpy.props.StringProperty(name="Name", options=PROP_OPTS, get=lambda self : self.value)
131    name   = bpy.props.StringProperty(name="Name", default="param", options={'HIDDEN'})
132    value  = bpy.props.StringProperty(name="Slot Name", options={'SKIP_SAVE'}, default="param", set=_s, get=lambda self: self.name)
MISCCOMMAND_PG_Param
name: str = 'param'
value: str = 'param'
Slot Name
@compat.BlRegister()
@generate_command_type_collections
class OBJECT_PG_CM3D2Menu(bpy.types.PropertyGroup):
434@compat.BlRegister()
435@generate_command_type_collections
436class OBJECT_PG_CM3D2Menu(bpy.types.PropertyGroup):
437    bl_idname = 'CM3D2Menu'
438
439    version     = bpy.props.IntProperty   (name="Version"    , options=PROP_OPTS, min=0, step=100    )
440    path        = bpy.props.StringProperty(name="Path"       , options=PROP_OPTS, subtype='FILE_PATH')
441    name        = bpy.props.StringProperty(name="Name"       , options=PROP_OPTS)
442    category    = bpy.props.StringProperty(name="Category"   , options=PROP_OPTS)
443    description = bpy.props.StringProperty(name="Description", options=PROP_OPTS)
444                                                                       
445    attach_point_commands = bpy.props.CollectionProperty(type=CM3D2MENU_PG_AttachPointCommand, options={'HIDDEN'})
446    property_commands     = bpy.props.CollectionProperty(type=CM3D2MENU_PG_PropertyCommand   , options={'HIDDEN'})
447    misc_commands         = bpy.props.CollectionProperty(type=CM3D2MENU_PG_MiscCommand       , options={'HIDDEN'})
448
449    commands = bpy.props.CollectionProperty(name="Commands", type=CM3D2MENU_PG_CommandPointer, options=PROP_OPTS)
450    active_index = bpy.props.IntProperty(name="Active Command Index", options=PROP_OPTS, default=0)
451    
452    # NOTE : This dictionary is generated by @generate_command_type_collections
453    #command_type_collections = {
454    #    'CM3D2MenuAttachPointCommand'   : 'attach_point_commands',
455    #    'CM3D2MenuPropertyCommand'      : 'property_commands'    ,
456    #    ...
457    #    for all Collection Properties (except 'commands')
458    #}
459
460    updated = bpy.props.BoolProperty(options={'HIDDEN', 'SKIP_SAVE'}, default=False)
461    def update(self):
462        for index, command_pointer in enumerate(self.commands):
463            command = command_pointer.dereference(self)
464            command.index = index
465        updated = True
466
467    def get_active_command(self):
468        if len(self.commands) <= self.active_index:
469            return None
470        command_pointer = self.commands[self.active_index]
471        return command_pointer.dereference(self)
472
473    def new_command(self, command: str):
474        command_type = COMMAND_TYPE_LIST.get(command)
475        collection_name = 'misc_commands'
476        if command_type:
477            collection_name = self.command_type_collections.get(command_type.bl_idname) or collection_name
478        
479        collection = getattr(self, collection_name)
480        new_command = collection.add()
481        new_command.command = command
482        
483        new_pointer = self.commands.add()
484        new_pointer.collection_name = collection_name
485        new_pointer.prop_index = len(collection) - 1
486
487        new_command.index = len(self.commands) - 1
488
489        return new_command
490
491    def remove_command(self, index: int):
492        command_pointer = self.commands[index]
493        command = command_pointer.dereference(self)
494        
495        collection_name = self.command_type_collections.get(command.bl_idname) or 'misc_commands'
496        collection = getattr(self, collection_name)
497
498        prop_index = command_pointer.prop_index
499        self.commands.remove(index)
500        self.update()
501        collection.remove(prop_index)
502
503        for i, c in enumerate(collection):
504            self.commands[c.index].prop_index = i
505
506        if self.active_index >= len(self.commands):
507            self.active_index = len(self.commands) - 1
508
509    def move_command(self, old_index, new_index, update=True):
510        self.commands.move(old_index, new_index)
511        self.updated = False
512        if update:
513            self.update()
514
515    def parse_list(self, string_list):
516        command = string_list[0]
517        new_command = self.new_command(command)
518        new_command.parse_list(string_list)
519
520    def unpack_from_file(self, file):
521        if common.read_str(file) != 'CM3D2_MENU':
522            raise IOError("Not a valid CM3D2 .menu file.")
523
524        self.version      = struct.unpack('<i', file.read(4))[0]
525        self.path         = common.read_str(file)
526        self.name         = common.read_str(file)
527        self.category     = common.read_str(file)
528        self.description  = common.read_str(file)
529        
530        struct.unpack('<i', file.read(4))[0]
531        string_list = []
532        string_list_length = struct.unpack('<B', file.read(1))[0]
533        while string_list_length > 0:
534            string_list.clear()
535
536            for i in range(string_list_length):
537                string_list.append(common.read_str(file))
538            
539            try:
540                self.parse_list(string_list)
541            except ValueError as e:
542                print(e)
543            
544            # Check for end of file
545            chunk = file.read(1)
546            if len(chunk) == 0:
547                break
548            string_list_length = struct.unpack('<B', chunk)[0]
549
550        self.update()
551    
552    def pack_into_file(self, file):
553        self.update()
554
555        common.write_str(file, 'CM3D2_MENU')
556
557        file.write(struct.pack('<i', self.version    ))
558        common.write_str(file,       self.path       )
559        common.write_str(file,       self.name       )
560        common.write_str(file,       self.category   )
561        common.write_str(file,       self.description)
562                    
563        buffer = bytearray()
564        for command_pointer in self.commands:
565            buffer = command_pointer.dereference(self).pack_into(buffer)
566        buffer = buffer + struct.pack('<B', 0x00)
567        
568        file.write(struct.pack('<i', len(buffer)))
569        file.write(bytes(buffer))
570
571    def clear(self):
572        self.property_unset('version'    )
573        self.property_unset('path'       )
574        self.property_unset('name'       )
575        self.property_unset('category'   )
576        self.property_unset('description')
577        
578        for prop in self.command_type_collections.values():
579            self.property_unset(prop)
580
581        self.property_unset('misc_commands')
582
583        self.property_unset('commands'    )
584        self.property_unset('active_index')
585
586        self.property_unset('updated')
OBJECT_PG_CM3D2Menu
active_index: int = 0
Active Command Index
attach_point_commands: dict[str, CM3D2 Converter.menu_file.CM3D2MENU_PG_AttachPointCommand]
category: str = ''
Category
commands: dict[str, CM3D2 Converter.menu_file.CM3D2MENU_PG_CommandPointer]
Commands
description: str = ''
Description
misc_commands: dict[str, CM3D2 Converter.menu_file.CM3D2MENU_PG_MiscCommand]
name: str = ''
Name
path: str = ''
Path
property_commands: dict[str, CM3D2 Converter.menu_file.CM3D2MENU_PG_PropertyCommand]
updated: bool = False
version: int = 0
Version