Exemple #1
0
class SvProfileNode(bpy.types.Node, SverchCustomTreeNode):
    '''
    SvProfileNode generates one or more profiles / elevation segments using;
    assignments, variables, and a string descriptor similar to SVG.

    This node expects simple input, or vectorized input.
    - sockets with no input are automatically 0, not None
    - The longest input array will be used to extend the shorter ones, using last value repeat.
    '''
    bl_idname = 'SvProfileNode'
    bl_label = 'ProfileNode'
    bl_icon = 'OUTLINER_OB_EMPTY'

    def mode_change(self, context):
        if not (self.selected_axis == self.current_axis):
            self.label = self.selected_axis
            self.current_axis = self.selected_axis
            updateNode(self, context)

    axis_options = [
        ("X", "X", "", 0),
        ("Y", "Y", "", 1),
        ("Z", "Z", "", 2)
    ]
    current_axis = StringProperty(default='Z')

    selected_axis = EnumProperty(
        items=axis_options,
        name="Type of axis",
        description="offers basic axis output vectors X|Y|Z",
        default="Z",
        update=mode_change)

    profile_file = StringProperty(default="", update=updateNode)
    filename = StringProperty(default="", update=updateNode)
    posxy = FloatVectorProperty(default=(0.0, 0.0), size=2)
    extended_parsing = BoolProperty(default=False)

    def draw_buttons(self, context, layout):
        row = layout.row()
        row.prop(self, 'selected_axis', expand=True)
        row = layout.row(align=True)
        # row.prop(self, "profile_file", text="")
        row.prop_search(self, 'filename', bpy.data, 'texts', text='', icon='TEXT')

    def draw_buttons_ext(self, context, layout):
        row = layout.row(align=True)
        row.prop(self, "extended_parsing", text="extended parsing")

    def init(self, context):
        self.inputs.new('StringsSocket', "a", "a")
        self.inputs.new('StringsSocket', "b", "b")

        self.outputs.new('VerticesSocket', "Verts", "Verts")
        self.outputs.new('StringsSocket', "Edges", "Edges")

    def adjust_inputs(self):
        '''
        takes care of adding new inputs until reaching 26,
        '''
        inputs = self.inputs
        if inputs[-1].links:
            new_index = len(inputs)
            new_letter = idx_map.get(new_index, None)
            if new_letter:
                inputs.new('StringsSocket', new_letter, new_letter)
            else:
                print('this implementation goes up to 26 chars only, use SN or EK')
                print('- or contact Dealga')
        elif not inputs[-2].links:
            inputs.remove(inputs[-1])

    def update(self):
        '''
        update analyzes the state of the node and returns if the criteria to start processing
        are not met.
        '''

        # keeping the file internal for now.
        # self.filename = self.profile_file.strip()
        if not (self.filename in bpy.data.texts):
            return

        if not ('Edges' in self.outputs):
            return

        elif len([1 for inputs in self.inputs if inputs.links]) == 0:
            ''' must have at least one input... '''
            return

        self.adjust_inputs()

        # 0 == verts, this is a minimum requirement.
        if not self.outputs[0].links:
            return

        self.process()

    def homogenize_input(self, segments, longest):
        '''
        edit segments in place, extend all to match length of longest
        '''
        for letter, letter_dict in segments.items():
            if letter_dict['length'] < longest:
                fullList(letter_dict['data'], longest)

    def meta_get(self, s_name, fallback, level):
        '''
        private function for the get_input function, accepts level 0..2
        - if socket has no links, then return fallback value
        - s_name can be an index instead of socket name
        '''
        inputs = self.inputs
        if inputs[s_name].links:
            socket_in = SvGetSocketAnyType(self, inputs[s_name])
            if level == 1:
                data = dataCorrect(socket_in)[0]
            elif level == 2:
                data = dataCorrect(socket_in)[0][0]
            else:
                data = dataCorrect(socket_in)
            return data
        else:
            return fallback

    def get_input(self):
        '''
        collect all input socket data, and track the longest sequence.
        '''
        segments = {}
        longest = 0
        for i, input_ in enumerate(self.inputs):
            letter = idx_map[i]

            ''' get socket data, or use a fallback '''
            data = self.meta_get(i, [0], 2)

            num_datapoints = len(data)
            segments[letter] = {'length': num_datapoints, 'data': data}

            if num_datapoints > longest:
                longest = num_datapoints

        return segments, longest

    def process(self):
        segments, longest = self.get_input()

        if longest < 1:
            print('logic error, longest < 1')
            return

        self.homogenize_input(segments, longest)
        full_result_verts = []
        full_result_edges = []

        for idx in range(longest):
            path_object = PathParser(self, segments, idx)
            vertices, edges = path_object.get_geometry()

            axis_fill = {
                'X': lambda coords: (0, coords[0], coords[1]),
                'Y': lambda coords: (coords[0], 0, coords[1]),
                'Z': lambda coords: (coords[0], coords[1], 0)
                }.get(self.current_axis)

            vertices = list(map(axis_fill, vertices))
            full_result_verts.append(vertices)
            full_result_edges.append(edges)

        if full_result_verts:
            SvSetSocketAnyType(self, 'Verts', full_result_verts)

            if self.outputs['Edges'].links:
                SvSetSocketAnyType(self, 'Edges', full_result_edges)
Exemple #2
0
class BGIS_PREFS(AddonPreferences):

    bl_idname = PKG

    ################
    #Predefinate Spatial Ref. Systems

    def listPredefCRS(self, context):
        return PredefCRS.getEnumItems()

    #store crs preset as json string into addon preferences
    predefCrsJson: StringProperty(default=json.dumps(PREDEF_CRS))

    predefCrs: EnumProperty(
        name="Predefinate CRS",
        description="Choose predefinite Coordinate Reference System",
        items=listPredefCRS)

    ################
    #proj engine

    def getProjEngineItems(self, context):
        items = [('AUTO', 'Auto detect',
                  'Auto select the best library for reprojection tasks')]
        if HAS_GDAL:
            items.append(('GDAL', 'GDAL', 'Force GDAL as reprojection engine'))
        if HAS_PYPROJ:
            items.append(
                ('PYPROJ', 'pyProj', 'Force pyProj as reprojection engine'))
        #if EPSGIO.ping(): #too slow
        #	items.append( ('EPSGIO', 'epsg.io', '') )
        items.append(
            ('EPSGIO', 'epsg.io', 'Force epsg.io as reprojection engine'))
        items.append(('BUILTIN', 'Built in',
                      'Force reprojection through built in Python functions'))
        return items

    def updateProjEngine(self, context):
        prefs = getSettings()
        prefs['proj_engine'] = self.projEngine
        setSettings(prefs)

    projEngine: EnumProperty(name="Projection engine",
                             description="Select projection engine",
                             items=getProjEngineItems,
                             update=updateProjEngine)

    ################
    #img engine

    def getImgEngineItems(self, context):
        items = [('AUTO', 'Auto detect',
                  'Auto select the best imaging library')]
        if HAS_GDAL:
            items.append(
                ('GDAL', 'GDAL', 'Force GDAL as image processing engine'))
        if HAS_IMGIO:
            items.append(('IMGIO', 'ImageIO',
                          'Force ImageIO as image processing  engine'))
        if HAS_PIL:
            items.append(
                ('PIL', 'PIL', 'Force PIL as image processing  engine'))
        return items

    def updateImgEngine(self, context):
        prefs = getSettings()
        prefs['img_engine'] = self.imgEngine
        setSettings(prefs)

    imgEngine: EnumProperty(name="Image processing engine",
                            description="Select image processing engine",
                            items=getImgEngineItems,
                            update=updateImgEngine)

    ################
    #OSM

    osmTagsJson: StringProperty(
        default=json.dumps(OSM_TAGS))  #just a serialized list of tags

    def listOsmTags(self, context):
        prefs = context.preferences.addons[PKG].preferences
        tags = json.loads(prefs.osmTagsJson)
        #put each item in a tuple (key, label, tooltip)
        return [(tag, tag, tag) for tag in tags]

    osmTags: EnumProperty(name="OSM tags",
                          description="List of registered OSM tags",
                          items=listOsmTags)

    overpassServer: EnumProperty(
        name="Overpass server",
        description="Select an overpass server",
        default="https://lz4.overpass-api.de/api/interpreter",
        items=
        [("https://lz4.overpass-api.de/api/interpreter", 'overpass-api.de',
          'Main Overpass API instance'),
         ("http://overpass.openstreetmap.fr/api/interpreter",
          'overpass.openstreetmap.fr', 'French Overpass API instance'),
         ("https://overpass.kumi.systems/api/interpreter",
          'overpass.kumi.systems', 'Kumi Systems Overpass Instance')
         #("https://overpass.nchc.org.tw", 'overpass.nchc.org.tw', 'Taiwan Overpass API')
         ])

    ################
    #Basemaps

    def getCacheFolder(self):
        return bpy.path.abspath(self.get("cacheFolder", ''))

    def setCacheFolder(self, value):
        self["cacheFolder"] = value

    cacheFolder: StringProperty(
        name="Cache folder",
        default="",
        description="Define a folder where to store Geopackage SQlite db",
        subtype='DIR_PATH',
        get=getCacheFolder,
        set=setCacheFolder)

    synchOrj: BoolProperty(
        name="Synch. lat/long",
        description=
        'Keep geo origin synchronized with crs origin. Can be slow with remote reprojection services',
        default=True)

    zoomToMouse: BoolProperty(
        name="Zoom to mouse",
        description='Zoom towards the mouse pointer position',
        default=True)

    lockOrigin: BoolProperty(
        name="Lock origin",
        description='Do not move scene origin when panning map',
        default=False)
    lockObj: BoolProperty(
        name="Lock objects",
        description='Retain objects geolocation when moving map origin',
        default=True)

    resamplAlg: EnumProperty(
        name="Resampling method",
        description="Choose GDAL's resampling method used for reprojection",
        items=[('NN', 'Nearest Neighboor', ''), ('BL', 'Bilinear', ''),
               ('CB', 'Cubic', ''), ('CBS', 'Cubic Spline', ''),
               ('LCZ', 'Lanczos', '')])

    ################
    #DEM options
    srtmServer: EnumProperty(
        name="SRTM server",
        description="Select an overpass server",
        items=
        [("https://portal.opentopography.org/API/globaldem?demtype=SRTMGL1&west={W}&east={E}&south={S}&north={N}&outputFormat=GTiff",
          'OpenTopography SRTM 30m',
          'OpenTopography.org web service for SRTM 30m global DEM'),
         ("https://portal.opentopography.org/API/globaldem?demtype=SRTMGL3&west={W}&east={E}&south={S}&north={N}&outputFormat=GTiff",
          'OpenTopography SRTM 90m',
          'OpenTopography.org web service for SRTM 90m global DEM'),
         ("http://www.marine-geo.org/services/GridServer?west={W}&east={E}&south={S}&north={N}&layer=topo&format=geotiff&resolution=high",
          'Marine-geo.org',
          'Marine-geo.org web service for GMRT global DEM (terrestrial (ASTER) and bathymetry)'
          )])

    ################
    #IO options
    mergeDoubles: BoolProperty(
        name="Merge duplicate vertices",
        description=
        'Merge shared vertices between features when importing vector data',
        default=False)
    adjust3Dview: BoolProperty(
        name="Adjust 3D view",
        description=
        "Update 3d view grid size and clip distances according to the new imported object's size",
        default=True)
    forceTexturedSolid: BoolProperty(
        name="Force textured solid shading",
        description="Update shading mode to display raster's texture",
        default=True)

    ################
    #System
    def updateLogLevel(self, context):
        logger = logging.getLogger(PKG)
        logger.setLevel(logging.getLevelName(self.logLevel))

    logLevel: EnumProperty(name="Logging level",
                           description="Select the logging level",
                           items=[('DEBUG', 'Debug', ''), ('INFO', 'Info', ''),
                                  ('WARNING', 'Warning', ''),
                                  ('ERROR', 'Error', ''),
                                  ('CRITICAL', 'Critical', '')],
                           update=updateLogLevel,
                           default='DEBUG')

    ################
    def draw(self, context):
        layout = self.layout

        #SRS
        box = layout.box()
        box.label(text='Spatial Reference Systems')
        row = box.row().split(factor=0.5)
        row.prop(self, "predefCrs", text='')
        row.operator("bgis.add_predef_crs", icon='ADD')
        row.operator("bgis.edit_predef_crs", icon='PREFERENCES')
        row.operator("bgis.rmv_predef_crs", icon='REMOVE')
        row.operator("bgis.reset_predef_crs", icon='PLAY_REVERSE')
        box.prop(self, "projEngine")
        box.prop(self, "imgEngine")

        #Basemaps
        box = layout.box()
        box.label(text='Basemaps')
        box.prop(self, "cacheFolder")
        row = box.row()
        row.prop(self, "zoomToMouse")
        row.prop(self, "lockObj")
        row.prop(self, "lockOrigin")
        row.prop(self, "synchOrj")
        row = box.row()
        row.prop(self, "resamplAlg")

        #IO
        box = layout.box()
        box.label(text='Import/Export')
        row = box.row().split(factor=0.5)
        split = row.split(factor=0.9, align=True)
        split.prop(self, "osmTags")
        split.operator(
            "wm.url_open", icon='INFO'
        ).url = "http://wiki.openstreetmap.org/wiki/Map_Features"
        row.operator("bgis.add_osm_tag", icon='ADD')
        row.operator("bgis.edit_osm_tag", icon='PREFERENCES')
        row.operator("bgis.rmv_osm_tag", icon='REMOVE')
        row.operator("bgis.reset_osm_tags", icon='PLAY_REVERSE')
        row = box.row()
        row.prop(self, "overpassServer")
        row.prop(self, "srtmServer")
        row = box.row()
        row.prop(self, "mergeDoubles")
        row.prop(self, "adjust3Dview")
        row.prop(self, "forceTexturedSolid")

        #System
        box = layout.box()
        box.label(text='System')
        box.prop(self, "logLevel")
Exemple #3
0
class SvLloydSolidNode(bpy.types.Node, SverchCustomTreeNode):
    """
    Triggers: Lloyd Solid
    Tooltip: Redistribute 3D points in the volume of a Solid body uniformly by use of Lloyd's algorithm
    """
    bl_idname = 'SvLloydSolidNode'
    bl_label = 'Lloyd in Solid'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_VORONOI'

    iterations: IntProperty(name="Iterations",
                            description="Number of Lloyd algorithm iterations",
                            min=0,
                            default=3,
                            update=updateNode)

    thickness: FloatProperty(name="Thickness",
                             default=1.0,
                             min=0.0,
                             update=updateNode)

    accuracy: IntProperty(name="Accuracy", default=5, min=1, update=updateNode)

    @throttle_and_update_node
    def update_sockets(self, context):
        self.inputs['Thickness'].hide_safe = self.mode != 'SURFACE'

    modes = [('VOLUME', "Volume",
              "Distribute points inside the volume of a Solid body", 0),
             ('SURFACE', "Surface",
              "Distribute points on the surface of a Solid body", 1)]

    mode: EnumProperty(name="Mode",
                       description="Where to distribute points",
                       items=modes,
                       default='VOLUME',
                       update=update_sockets)

    def draw_buttons(self, context, layout):
        layout.prop(self, "mode", text='')

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        layout.prop(self, "accuracy")

    def sv_init(self, context):
        self.inputs.new('SvSolidSocket', "Solid")
        self.inputs.new('SvVerticesSocket',
                        "Sites").enable_input_link_menu = False
        self.inputs.new('SvStringsSocket', 'Thickness').prop_name = 'thickness'
        self.inputs.new('SvStringsSocket',
                        'Iterations').prop_name = 'iterations'
        self.inputs.new('SvScalarFieldSocket',
                        'Weights').enable_input_link_menu = False
        self.outputs.new('SvVerticesSocket', "Sites")
        self.update_sockets(context)

    def process(self):

        if not any(socket.is_linked for socket in self.outputs):
            return

        solid_in = self.inputs['Solid'].sv_get()
        sites_in = self.inputs['Sites'].sv_get()
        iterations_in = self.inputs['Iterations'].sv_get()
        thickness_in = self.inputs['Thickness'].sv_get()
        weights_in = self.inputs['Weights'].sv_get(default=[[None]])

        solid_in = ensure_nesting_level(solid_in, 2, data_types=(Part.Shape, ))
        input_level = get_data_nesting_level(sites_in)
        sites_in = ensure_nesting_level(sites_in, 4)
        iterations_in = ensure_nesting_level(iterations_in, 2)
        thickness_in = ensure_nesting_level(thickness_in, 2)
        if self.inputs['Weights'].is_linked:
            weights_in = ensure_nesting_level(weights_in,
                                              2,
                                              data_types=(SvScalarField, ))

        nested_output = input_level > 3

        tolerance = 10**(-self.accuracy)

        verts_out = []
        for params in zip_long_repeat(solid_in, sites_in, iterations_in,
                                      thickness_in, weights_in):
            new_verts = []
            for solid, sites, iterations, thickness, weights in zip_long_repeat(
                    *params):
                if self.mode == 'VOLUME':
                    sites = lloyd_in_solid(solid,
                                           sites,
                                           iterations,
                                           weight_field=weights,
                                           tolerance=tolerance)
                else:
                    sites = lloyd_on_solid_surface(solid,
                                                   sites,
                                                   thickness,
                                                   iterations,
                                                   weight_field=weights,
                                                   tolerance=tolerance)

                new_verts.append(sites)
            if nested_output:
                verts_out.append(new_verts)
            else:
                verts_out.extend(new_verts)

        self.outputs['Sites'].sv_set(verts_out)
Exemple #4
0
class SelectHierarchy(Operator):
    """Select object relative to the active object's position """ \
    """in the hierarchy"""
    bl_idname = "object.select_hierarchy"
    bl_label = "Select Hierarchy"
    bl_options = {'REGISTER', 'UNDO'}

    direction = EnumProperty(
        items=(
            ('PARENT', "Parent", ""),
            ('CHILD', "Child", ""),
        ),
        name="Direction",
        description="Direction to select in the hierarchy",
        default='PARENT')

    extend = BoolProperty(
        name="Extend",
        description="Extend the existing selection",
        default=False,
    )

    @classmethod
    def poll(cls, context):
        return context.object

    def execute(self, context):
        select_new = []
        act_new = None

        selected_objects = context.selected_objects
        obj_act = context.object

        if context.object not in selected_objects:
            selected_objects.append(context.object)

        if self.direction == 'PARENT':
            for obj in selected_objects:
                parent = obj.parent

                if parent:
                    if obj_act == obj:
                        act_new = parent

                    select_new.append(parent)

        else:
            for obj in selected_objects:
                select_new.extend(obj.children)

            if select_new:
                select_new.sort(key=lambda obj_iter: obj_iter.name)
                act_new = select_new[0]

        # don't edit any object settings above this
        if select_new:
            if not self.extend:
                bpy.ops.object.select_all(action='DESELECT')

            for obj in select_new:
                obj.select = True

            context.scene.objects.active = act_new
            return {'FINISHED'}

        return {'CANCELLED'}
Exemple #5
0
class OBJECT_OT_add_mesh_rock(bpy.types.Operator):
    """Add rock objects"""
    bl_idname = "mesh.add_mesh_rock"
    bl_label = "Add Rocks"
    bl_options = {'REGISTER', 'UNDO'}
    bl_description = "Add rocks"

    # Get the preset values from the XML file.
    #   -> The script was morphed into a Python module
    #       to support this.
    # Tell settings.py to parse the XML file with the settings.
    # Then get the default values resulting from the parsing.
    # Make a list containing the default values and append to that
    # the presets specified in the same XML file.  This list will
    # be used to load preset values.
    settings.parse()
    defaults = settings.getDefault()
    presetsList = [defaults]
    presetsList += settings.getPresetLists()
    presets = []
    lastPreset = 0

    # Build the presets list for the enum property.
    # This needs to be a for loop as the user might add presets to
    # the XML file and those should show here:
    for i, preset in enumerate(presetsList):
        presets.append((str(i), preset[0], preset[0] + " preset values"))

    preset_values: EnumProperty(
        name="Presets",
        items=presets,
        description="Preset values for some rock types")

    num_of_rocks: IntProperty(
        name="Number of rocks",
        description=
        "Number of rocks to generate. WARNING: Slow at high values!",
        min=1,
        max=1048576,
        soft_max=20,
        default=1)

    scale_X: FloatVectorProperty(name="X scale",
                                 description="X axis scaling range",
                                 min=0.0,
                                 max=256.0,
                                 step=1,
                                 default=defaults[1],
                                 size=2)
    skew_X: FloatProperty(name="X skew",
                          description="X Skew ratio. 0.5 is no skew",
                          min=-1.0,
                          max=1.0,
                          default=defaults[4])
    scale_Y: FloatVectorProperty(name="Y scale",
                                 description="Y axis scaling range",
                                 min=.0,
                                 max=256.0,
                                 step=1,
                                 default=defaults[2],
                                 size=2)
    skew_Y: FloatProperty(name="Y skew",
                          description="Y Skew ratio. 0.5 is no skew",
                          min=-1.0,
                          max=1.0,
                          default=defaults[5])
    scale_Z: FloatVectorProperty(name="Z scale",
                                 description="Z axis scaling range",
                                 min=0.0,
                                 max=256.0,
                                 step=1,
                                 default=defaults[3],
                                 size=2)
    skew_Z: FloatProperty(name="Z skew",
                          description="Z Skew ratio. 0.5 is no skew",
                          min=-1.0,
                          max=1.0,
                          default=defaults[6])
    use_scale_dis: BoolProperty(
        name="Scale displace textures",
        description=
        "Scale displacement textures with dimensions.  May cause stretched textures",
        default=defaults[7])
    scale_fac: FloatVectorProperty(
        name="Scaling Factor",
        description="XYZ scaling factor.  1: no scaling",
        min=0.0001,
        max=256.0,
        step=0.1,
        default=defaults[8],
        size=3)

    # @todo Possible to title this section "Physical Properties:"?
    deform: FloatProperty(name="Deformation",
                          description="Rock deformation",
                          min=0.0,
                          soft_max=50,
                          max=1024.0,
                          default=defaults[9])
    rough: FloatProperty(name="Roughness",
                         description="Rock roughness",
                         min=0.0,
                         soft_max=50,
                         max=1024.0,
                         default=defaults[10])
    detail: IntProperty(
        name="Detail level",
        description="Detail level.  WARNING: Slow at high values!",
        min=1,
        soft_max=4,
        max=10,
        default=defaults[11])
    display_detail: IntProperty(
        name="Display Detail",
        description=
        "Display detail.  Use a lower value for high numbers of rocks",
        min=1,
        soft_max=4,
        max=10,
        default=defaults[12])
    smooth_fac: FloatProperty(
        name="Smooth Factor",
        description="Smoothing factor.  A value of 0 disables",
        min=0.0,
        max=128.0,
        default=defaults[13])
    smooth_it: IntProperty(
        name="Smooth Iterations",
        description="Smoothing iterations.  A value of 0 disables",
        min=0,
        max=50,
        default=defaults[14])

    use_generate: BoolProperty(name="Generate Rocks",
                               description="Enable actual generation",
                               default=defaults[15])
    use_random_seed: BoolProperty(
        name="Use a random seed",
        description=
        "Create a seed based on time. Causes user seed to be ignored",
        default=defaults[16])
    user_seed: IntProperty(name="User seed",
                           description="Use a specific seed for the generator",
                           min=0,
                           max=1048576,
                           default=defaults[17])

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        box.prop(self, 'num_of_rocks')
        box = layout.box()
        box.prop(self, 'scale_X')
        box.prop(self, 'skew_X')
        box.prop(self, 'scale_Y')
        box.prop(self, 'skew_Y')
        box.prop(self, 'scale_Z')
        box.prop(self, 'skew_Z')
        box.prop(self, 'use_scale_dis')
        if self.use_scale_dis:
            box.prop(self, 'scale_fac')
        else:
            self.scale_fac = utils.toFloats(self.defaults[8])
        box = layout.box()
        box.prop(self, 'deform')
        box.prop(self, 'rough')
        box.prop(self, 'detail')
        box.prop(self, 'display_detail')
        box.prop(self, 'smooth_fac')
        box.prop(self, 'smooth_it')

        box = layout.box()
        box.prop(self, 'use_generate')
        box.prop(self, 'use_random_seed')
        if not self.use_random_seed:
            box.prop(self, 'user_seed')
        box.prop(self, 'preset_values')

    def execute(self, context):
        # turn off 'Enter Edit Mode'
        use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
        bpy.context.preferences.edit.use_enter_edit_mode = False

        # The following "if" block loads preset values:
        if self.lastPreset != int(self.preset_values):
            self.scale_X = utils.toFloats(self.presetsList[int(
                self.preset_values)][1])
            self.scale_Y = utils.toFloats(self.presetsList[int(
                self.preset_values)][2])
            self.scale_Z = utils.toFloats(self.presetsList[int(
                self.preset_values)][3])
            self.skew_X = float(self.presetsList[int(self.preset_values)][4])
            self.skew_Y = float(self.presetsList[int(self.preset_values)][5])
            self.skew_Z = float(self.presetsList[int(self.preset_values)][6])
            self.use_scale_dis = bool(self.presetsList[int(
                self.preset_values)][7])
            self.scale_fac = utils.toFloats(self.presetsList[int(
                self.preset_values)][8])
            self.deform = float(self.presetsList[int(self.preset_values)][9])
            self.rough = float(self.presetsList[int(self.preset_values)][10])
            self.detail = int(self.presetsList[int(self.preset_values)][11])
            self.display_detail = int(self.presetsList[int(
                self.preset_values)][12])
            self.smooth_fac = float(self.presetsList[int(
                self.preset_values)][13])
            self.smooth_it = int(self.presetsList[int(self.preset_values)][14])
            self.use_generate = bool(self.presetsList[int(
                self.preset_values)][15])
            self.use_random_seed = bool(self.presetsList[int(
                self.preset_values)][16])
            self.user_seed = int(self.presetsList[int(self.preset_values)][17])
            self.lastPreset = int(self.preset_values)

        # todo Add deform, deform_Var, rough, and rough_Var:
        #   *** todo completed 4/23/2011 ***
        #   *** Eliminated "deform_Var" and "rough_Var" so the script is not
        #       as complex to use.  May add in again as advanced features. ***
        if self.use_generate:
            rocks = generateRocks(
                context, self.scale_X, self.skew_X, self.scale_Y, self.skew_Y,
                self.scale_Z, self.skew_Z, self.scale_fac, self.detail,
                self.display_detail, self.deform, self.rough, self.smooth_fac,
                self.smooth_it, self.num_of_rocks, self.user_seed,
                self.use_scale_dis, self.use_random_seed, use_enter_edit_mode)

        for rock in rocks:
            rock.select_set(True)

        if use_enter_edit_mode:
            bpy.ops.object.mode_set(mode='EDIT')

        # restore pre operator state
        bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode

        return {'FINISHED'}
class Solids(bpy.types.Operator):
    """Add one of the (regular) solids (mesh)"""
    bl_idname = "mesh.primitive_solid_add"
    bl_label = "(Regular) solids"
    bl_description = "Add one of the Platonic, Archimedean or Catalan solids"
    bl_options = {'REGISTER', 'UNDO', 'PRESET'}

    source = EnumProperty(items=(("4", "Tetrahedron",
                                  ""), ("6", "Hexahedron",
                                        ""), ("8", "Octahedron",
                                              ""), ("12", "Dodecahedron", ""),
                                 ("20", "Icosahedron", "")),
                          name="Source",
                          description="Starting point of your solid")
    size = FloatProperty(
        name="Size",
        description="Radius of the sphere through the vertices",
        min=0.01,
        soft_min=0.01,
        max=100,
        soft_max=100,
        default=1.0)
    vTrunc = FloatProperty(name="Vertex Truncation",
                           description="Ammount of vertex truncation",
                           min=0.0,
                           soft_min=0.0,
                           max=2.0,
                           soft_max=2.0,
                           default=0.0,
                           precision=3,
                           step=0.5)
    eTrunc = FloatProperty(name="Edge Truncation",
                           description="Ammount of edge truncation",
                           min=0.0,
                           soft_min=0.0,
                           max=1.0,
                           soft_max=1.0,
                           default=0.0,
                           precision=3,
                           step=0.2)
    snub = EnumProperty(items=(("None", "No Snub", ""),
                               ("Left", "Left Snub", ""), ("Right",
                                                           "Right Snub", "")),
                        name="Snub",
                        description="Create the snub version")
    dual = BoolProperty(name="Dual",
                        description="Create the dual of the current solid",
                        default=False)
    keepSize = BoolProperty(
        name="Keep Size",
        description="Keep the whole solid at a constant size",
        default=False)
    preset = EnumProperty(
        items=(("0", "Custom", ""), ("t4", "Truncated Tetrahedron", ""),
               ("r4", "Cuboctahedron", ""), ("t6", "Truncated Cube", ""),
               ("t8", "Truncated Octahedron",
                ""), ("b6", "Rhombicuboctahedron",
                      ""), ("c6", "Truncated Cuboctahedron", ""),
               ("s6", "Snub Cube", ""), ("r12", "Icosidodecahedron", ""),
               ("t12", "Truncated Dodecahedron",
                ""), ("t20", "Truncated Icosahedron",
                      ""), ("b12", "Rhombicosidodecahedron",
                            ""), ("c12", "Truncated Icosidodecahedron",
                                  ""), ("s12", "Snub Dodecahedron", ""),
               ("dt4", "Triakis Tetrahedron",
                ""), ("dr4", "Rhombic Dodecahedron",
                      ""), ("dt6", "Triakis Octahedron",
                            ""), ("dt8", "Tetrakis Hexahedron", ""),
               ("db6", "Deltoidal Icositetrahedron",
                ""), ("dc6", "Disdyakis Dodecahedron",
                      ""), ("ds6", "Pentagonal Icositetrahedron",
                            ""), ("dr12", "Rhombic Triacontahedron",
                                  ""), ("dt12", "Triakis Icosahedron", ""),
               ("dt20", "Pentakis Dodecahedron",
                ""), ("db12", "Deltoidal Hexecontahedron",
                      ""), ("dc12", "Disdyakis Triacontahedron",
                            ""), ("ds12", "Pentagonal Hexecontahedron", "")),
        name="Presets",
        description="Parameters for some hard names")

    # actual preset values
    p = {
        "t4": ["4", 2 / 3, 0, 0, "None"],
        "r4": ["4", 1, 1, 0, "None"],
        "t6": ["6", 2 / 3, 0, 0, "None"],
        "t8": ["8", 2 / 3, 0, 0, "None"],
        "b6": ["6", 1.0938, 1, 0, "None"],
        "c6": ["6", 1.0572, 0.585786, 0, "None"],
        "s6": ["6", 1.0875, 0.704, 0, "Left"],
        "r12": ["12", 1, 0, 0, "None"],
        "t12": ["12", 2 / 3, 0, 0, "None"],
        "t20": ["20", 2 / 3, 0, 0, "None"],
        "b12": ["12", 1.1338, 1, 0, "None"],
        "c12": ["20", 0.921, 0.553, 0, "None"],
        "s12": ["12", 1.1235, 0.68, 0, "Left"],
        "dt4": ["4", 2 / 3, 0, 1, "None"],
        "dr4": ["4", 1, 1, 1, "None"],
        "dt6": ["6", 2 / 3, 0, 1, "None"],
        "dt8": ["8", 2 / 3, 0, 1, "None"],
        "db6": ["6", 1.0938, 1, 1, "None"],
        "dc6": ["6", 1.0572, 0.585786, 1, "None"],
        "ds6": ["6", 1.0875, 0.704, 1, "Left"],
        "dr12": ["12", 1, 0, 1, "None"],
        "dt12": ["12", 2 / 3, 0, 1, "None"],
        "dt20": ["20", 2 / 3, 0, 1, "None"],
        "db12": ["12", 1.1338, 1, 1, "None"],
        "dc12": ["20", 0.921, 0.553, 1, "None"],
        "ds12": ["12", 1.1235, 0.68, 1, "Left"]
    }

    # previous preset, for User-friendly reasons
    previousSetting = ""

    def execute(self, context):
        # turn off undo for better performance (3-5x faster), also makes sure
        #  that mesh ops are undoable and entire script acts as one operator
        bpy.context.user_preferences.edit.use_global_undo = False

        # piece of code to make presets remain until parameters are changed
        if self.preset != "0":
            # if preset, set preset
            if self.previousSetting != self.preset:
                using = self.p[self.preset]
                self.source = using[0]
                self.vTrunc = using[1]
                self.eTrunc = using[2]
                self.dual = using[3]
                self.snub = using[4]
            else:
                using = self.p[self.preset]
                result0 = self.source == using[0]
                result1 = abs(self.vTrunc - using[1]) < 0.004
                result2 = abs(self.eTrunc - using[2]) < 0.0015
                result4 = using[4] == self.snub or (
                    (using[4] == "Left") and self.snub in ["Left", "Right"])
                if (result0 and result1 and result2 and result4):
                    if self.p[self.previousSetting][3] != self.dual:
                        if self.preset[0] == "d":
                            self.preset = self.preset[1:]
                        else:
                            self.preset = "d" + self.preset
                else:
                    self.preset = "0"

        self.previousSetting = self.preset

        # generate mesh
        verts, faces = createSolid(self.source, self.vTrunc, self.eTrunc,
                                   self.dual, self.snub)

        # turn n-gons in quads and tri's
        faces = createPolys(faces)

        # resize to normal size, or if keepSize, make sure all verts are of length 'size'
        if self.keepSize:
            rad = self.size / verts[-1 if self.dual else 0].length
        else:
            rad = self.size
        verts = [i * rad for i in verts]

        # generate object
        # Create new mesh
        mesh = bpy.data.meshes.new("Solid")

        # Make a mesh from a list of verts/edges/faces.
        mesh.from_pydata(verts, [], faces)

        # Update mesh geometry after adding stuff.
        mesh.update()

        object_data_add(context, mesh, operator=None)
        # object generation done

        # turn undo back on
        bpy.context.user_preferences.edit.use_global_undo = True

        return {'FINISHED'}
class ImportGLTF2(Operator, ImportHelper):
    """Load a glTF 2.0 file"""
    bl_idname = 'import_scene.gltf'
    bl_label = 'Import glTF 2.0'

    filter_glob = StringProperty(default="*.glb;*.gltf", options={'HIDDEN'})

    loglevel = IntProperty(
        name='Log Level',
        description="Log Level")

    import_pack_images = BoolProperty(
        name='Pack images',
        description='Pack all images into .blend file',
        default=True
    )

    import_shading = EnumProperty(
        name="Shading",
        items=(("NORMALS", "Use Normal Data", ""),
               ("FLAT", "Flat Shading", ""),
               ("SMOOTH", "Smooth Shading", "")),
        description="How normals are computed during import",
        default="NORMALS")

    def draw(self, context):
        layout = self.layout

        layout.prop(self, 'import_pack_images')
        layout.prop(self, 'import_shading')

    def execute(self, context):
        return self.import_gltf2(context)

    def import_gltf2(self, context):
        import time
        from .io.imp.gltf2_io_gltf import glTFImporter
        from .blender.imp.gltf2_blender_gltf import BlenderGlTF

        self.set_debug_log()
        import_settings = self.as_keywords()

        self.gltf_importer = glTFImporter(self.filepath, import_settings)
        success, txt = self.gltf_importer.read()
        if not success:
            self.report({'ERROR'}, txt)
            return {'CANCELLED'}
        success, txt = self.gltf_importer.checks()
        if not success:
            self.report({'ERROR'}, txt)
            return {'CANCELLED'}
        self.gltf_importer.log.critical("Data are loaded, start creating Blender stuff")
        start_time = time.time()
        BlenderGlTF.create(self.gltf_importer)
        elapsed_s = "{:.2f}s".format(time.time() - start_time)
        self.gltf_importer.log.critical("glTF import finished in " + elapsed_s)
        self.gltf_importer.log.removeHandler(self.gltf_importer.log_handler)

        return {'FINISHED'}

    def set_debug_log(self):
        import logging
        if bpy.app.debug_value == 0:
            self.loglevel = logging.CRITICAL
        elif bpy.app.debug_value == 1:
            self.loglevel = logging.ERROR
        elif bpy.app.debug_value == 2:
            self.loglevel = logging.WARNING
        elif bpy.app.debug_value == 3:
            self.loglevel = logging.INFO
        else:
            self.loglevel = logging.NOTSET
class JK_OT_MMT_Load_Templates(bpy.types.Operator):
    """Loads Mr Mannequins template armatures, materials and meshes"""
    bl_idname = "jk.load_templates"
    bl_label = "Mr Mannequins Templates"
    bl_options = {'REGISTER', 'UNDO'}

    def update_rigging(self, context):
        if self.rigging:
            self.controls = True

    def update_controls(self, context):
        if not self.controls:
            self.rigging = False

    flavour: EnumProperty(name="Type", description="The type of template to load",
        items=[('BIPED', 'Biped', "The Mannequin, Femmequin and any other bipeds i create"),
            #('QUADRUPED', 'Quadruped', "The Equinequin and any other quadrupeds i create"),
            ('EQUIPMENT', 'Equipment', "The First Person Gun, Bow and Arrow and any other weapons/armour i create")],
        default='BIPED', options=set())
    
    bipeds: EnumProperty(name="Biped", description="The template to load",
        items=[('Mannequin', 'Mannequin (Epic)', "The mannequin in UE4s Third Person template"),
            ('Femmequin', 'Femmequin (Kroovy)', "The female mannequin Jim created from the the mannequin in UE4s Third Person template, has breast bones but needs an update"),
            ('Mannequin_Female', 'Mannequin Female (Epic)', "The female mannequin Epic created for the Third Person template in engine version 4.26, has broken wrist meshes (not my fault, it's what's in the engine)")],
        default='Mannequin', options=set())

    quadrupeds: EnumProperty(name="Quadruped", description="The template to load",
        items=[('Equinequin', 'Equinequin', "The horse Jim created from the mannequin in UE4s Third Person template")],
        default='Equinequin', options=set())

    equipment: EnumProperty(name="Equipment", description="The template to load",
        items=[('Gun', 'First Person Gun', "The gun in UE4s First Person template"),
            ('Bow', 'Bow and Arrow', "The bow and arrow Jim created from the gun in UE4s First Person template"),
            ('Sword_1H', 'One Handed Sword', "The one handed sword Jim created from the gun in UE4s First Person template"),
            ('Shield', 'Shield', "The shield Jim created from the gun in UE4s First Person template")],
        default='Gun', options=set())

    armatures: BoolProperty(name="Armatures", description="Load any armatures associated with this template",
        default=True, options=set())

    controls: BoolProperty(name="Use Controls", description="Load armatures with control bones",
        default=True, options=set(), update=update_controls)

    rigging: BoolProperty(name="Use Rigging", description="Load armatures with control bones and rigging",
        default=True, options=set(), update=update_rigging)
    
    meshes: BoolProperty(name="Meshes", description="Load any meshes associated with this template",
        default=True, options=set())
    
    materials: BoolProperty(name="Materials", description="Load any materials associated with this template",
        default=True, options=set())

    actions: BoolProperty(name="Actions", description="(Coming Soon!) Load any default actions associated with this template",
        default=False, options=set())

    rescale: BoolProperty(name="Use Scene Scale", description="Scale the template to match the current scenes unit scale",
        default=True, options=set())

    lods: BoolProperty(name="Load LODs", description="Load any level of detail meshes associated with this template",
        default=False, options=set())

    link: BoolProperty(name="Linked", description="Link the template and its data. (limits editing but reduces file size)",
        default=False, options=set())

    instance: BoolProperty(name="Instance Meshes", description="Instance meshes from existing ones. (if there are existing ones)",
        default=False, options=set())

    remap: BoolProperty(name="Remap Materials", description="Remap the loaded materials and images to use existing ones. (if there are existing ones)",
        default=False, options=set())

    #existing: BoolProperty(name="Existing Actions", description="Use existing actions of the same names. (if there are existing ones)",
        #default=False, options=set())

    def execute(self, context):
        
        # dictionary of the contents of the resources folder...
        templates = {
            # Default UE4 templates...
            'Mannequin' : {
                'armatures' : {"UE4_Mannequin_Skeleton" : ["UE4_Mannequin_Skeleton", "UE4_Mannequin_Skeleton_Controls", "UE4_Mannequin_Skeleton_Rigging"]},
                'meshes' : {"SK_Mannequin" : ["SK_Mannequin_LOD0", "SK_Mannequin_LOD1", "SK_Mannequin_LOD2", "SK_Mannequin_LOD3"]},
                'materials' : {"M_Male_Body" : 0, "M_UE4Man_ChestLogo" : 1},
                'actions' : {"" : []}
            },
            
            'Mannequin_Female' : {
                'armatures' : {"UE4_Mannequin_Skeleton_Female" : ["UE4_Mannequin_Female_Skeleton", "UE4_Mannequin_Female_Skeleton_Controls", "UE4_Mannequin_Female_Skeleton_Rigging"]},
                'meshes' : {"SK_Mannequin_Female" : ["SK_Mannequin_Female_LOD0"]},
                'materials' : {"MI_Female_Body" : 0, "M_UE4Man_ChestLogo" : 1},
                'actions' : {"" : []}
            },
            
            'Gun' : {
                'armatures' : {"UE4_FPGun_Skeleton" : ["UE4_FPGun_Skeleton", "UE4_FPGun_Skeleton_Controls", "UE4_FPGun_Skeleton_Rigging"]},
                'meshes' : {"SK_FPGun" : ["SK_FPGun_LOD0", "SK_FPGun_LOD1", "SK_FPGun_LOD2", "SK_FPGun_LOD3"]},
                'materials' : {"M_FPGun" : 0},
                'actions' : {"" : []}
            },
            
            # My custom templates...
            'Femmequin' : {
                'armatures' : {"UE4_Femmequin_Skeleton" : ["UE4_Femmequin_Skeleton", "UE4_Femmequin_Skeleton_Controls", "UE4_Femmequin_Skeleton_Rigging"]},
                'meshes' : {"SK_Femmequin" : ["SK_Femmequin_LOD0"]},
                'materials' : {"M_Male_Body" : 0, "M_UE4Man_ChestLogo" : 1},
                'actions' : {"" : []}
            },
            
            'Bow' : {
                'armatures' : {"UE4_FPBow_Skeleton" : ["UE4_FPBow_Skeleton", "UE4_FPBow_Skeleton_Controls", "UE4_FPBow_Skeleton_Rigging"]},
                'meshes' : {"SK_FPBow" : ["SK_FPBow_LOD0", "SK_FPArrow_LOD0"]},
                'materials' : {"M_FPGun" : 0},
                'actions' : {"" : []}
            },
            
            'Sword_1H' : {
                'armatures' : {},
                'meshes' : {"ST_Sword_1H" : ["ST_Sword_1H_LOD0"]},
                'materials' : {"M_FPGun" : 0},
                'actions' : {"" : []}
            },
                
            'Shield' : {
                'armatures' : {},
                'meshes' : {"ST_Shield" : ["ST_Shield_LOD0"]},
                'materials' : {"M_FPGun" : 0},
                'actions' : {"" : []}
            },
            
        }
        
        _functions_.load_template(self, templates)
        return {'FINISHED'}
    
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        template = self.bipeds if self.flavour == 'BIPED' else self.quadrupeds if self.flavour == 'QUADRUPED' else self.equipment
        
        template_uses_armatures = {'Gun' : True, 'Bow' : True, 'Sword_1H' : False, 'Shield' : False, 
            'Mannequin' : True, 'Femmequin' : True, 'Mannequin_Female' : True}
        template_uses_actions = {'Gun' : False, 'Bow' : False, 'Sword_1H' : False, 'Shield' : False, 
            'Mannequin' : False, 'Femmequin' : False, 'Mannequin_Female' : False}
        
        layout = self.layout
        row = layout.row()
        row.prop(self, "flavour", text="")
        row.prop(self, "rescale")
        row = layout.row()
        if self.flavour == 'BIPED':
            row.prop(self, "bipeds", text="")
        elif self.flavour == 'QUADRUPED':
            row.prop(self, "quadrupeds", text="")
        elif self.flavour == 'EQUIPMENT':
            row.prop(self, "equipment", text="")

        row = layout.row(align=False)
        arm_col = row.column(align=True)
        arm_row = arm_col.row(align=True)
        arm_row.prop(self, "armatures", toggle=True)
        arm_row.prop(self, "controls", toggle=True, text="", icon='BONE_DATA')
        arm_row.prop(self, "rigging", toggle=True, text="", icon='CONSTRAINT_BONE')
        arm_col.enabled = template_uses_armatures[template]
        split = row.split()
        act_col = split.column(align=True)
        act_row = act_col.row(align=True)
        act_row.prop(self, "actions", toggle=True)
        act_row.prop(self, "actions", toggle=True, text="", icon='CON_ACTION')
        act_col.enabled = template_uses_actions[template]

        row = layout.row(align=False)
        me_col = row.column(align=True)
        me_row = me_col.row(align=True)
        me_row.prop(self, "meshes", toggle=True)
        me_row.prop(self, "instance", toggle=True, text="", icon='MOD_DATA_TRANSFER')
        me_row.prop(self, "lods", toggle=True, text="", icon='MOD_DECIM')
        split = row.split()
        ma_col = split.column(align=True)
        ma_row = ma_col.row(align=True)
        ma_row.prop(self, "materials", toggle=True)
        ma_row.prop(self, "remap", toggle=True, text="", icon='IMAGE_REFERENCE')
        
        
        
        """col = row.column(align=True)
        arma_row = col.row(align=True)
        arma_row.prop(self, "armatures", toggle=True)
        arma_row.prop(self, "controls", toggle=True, text="", icon='BONE_DATA')
        arma_row.prop(self, "rigging", toggle=True, text="", icon='CONSTRAINT_BONE')
        row.prop(self, "meshes", toggle=True)
        row.prop(self, "materials", toggle=True)
        row.prop(self, "actions", toggle=True)
        row = layout.row()
        row.prop(self, "rescale")
        row.prop(self, "lods")
        row.prop(self, "instance")
        row.prop(self, "remap")"""
        

        
class KillBgProcess(bpy.types.Operator):
    '''Remove processes in background'''
    bl_idname = "object.kill_bg_process"
    bl_label = "Kill Background Process"
    bl_options = {'REGISTER'}

    process_type: EnumProperty(
        name="Type",
        items=process_types,
        description="Type of process",
        default="UPLOAD",
    )

    process_source: EnumProperty(
        name="Source",
        items=process_sources,
        description="Source of process",
        default="MODEL",
    )

    def execute(self, context):
        s = bpy.context.scene

        cls = bpy.ops.object.convert.__class__
        # first do the easy stuff...TODO all cases.
        props = utils.get_upload_props()
        if self.process_type == 'UPLOAD':
            props.uploading = False
        if self.process_type == 'THUMBNAILER':
            props.is_generating_thumbnail = False
        global blenderkit_bg_process
        # print('killing', self.process_source, self.process_type)
        # then go kill the process. this wasn't working for unsetting props and that was the reason for changing to the method above.

        processes = bg_processes
        for p in processes:

            tcom = p[1]
            # print(tcom.process_type, self.process_type)
            if tcom.process_type == self.process_type:
                source = eval(tcom.eval_path)
                kill = False
                #TODO HDR - add killing of process
                if source.bl_rna.name == 'Object' and self.process_source == 'MODEL':
                    if source.name == bpy.context.active_object.name:
                        kill = True
                if source.bl_rna.name == 'Scene' and self.process_source == 'SCENE':
                    if source.name == bpy.context.scene.name:
                        kill = True
                if source.bl_rna.name == 'Image' and self.process_source == 'HDR':
                    ui_props = bpy.context.scene.blenderkitUI
                    if source.name == ui_props.hdr_upload_image.name:
                        kill = False

                if source.bl_rna.name == 'Material' and self.process_source == 'MATERIAL':
                    if source.name == bpy.context.active_object.active_material.name:
                        kill = True
                if source.bl_rna.name == 'Brush' and self.process_source == 'BRUSH':
                    brush = utils.get_active_brush()
                    if brush is not None and source.name == brush.name:
                        kill = True
                if kill:
                    estring = tcom.eval_path_computing + ' = False'
                    exec(estring)
                    processes.remove(p)
                    tcom.proc.kill()

        return {'FINISHED'}
class VIEW3D_OT_ntzbu_group_with_empty(Operator):
    """Tooltip"""
    bl_idname           = "view3d.ntzbu_group_with_empty"
    bl_label            = "NTZBU : Group With Empty"
    bl_description      = "Parent a selection of objects to an invisible empty so that they may be moved/rotated/scaled similar to how Autodesk Maya groups objects"
    bl_options          = {'REGISTER', 'UNDO'}

    bUseOverridesFromAddonPrefs : BoolProperty(
        name="Use Overrides from Add-on Preferences",
        description='Gets operator properties from the addon-preferences.  This will override any settings in the user customized keymap.  If you want to prevent the addon preferences from setting your operator properties, set this to False',
        default = True
    )

    modeAtBegin         = "OBJECT"
    selObjNames         = []
    cursorLocAtBegin    = None
    activeObjName       = None

    showOperatorOptions : BoolProperty (
        name            = 'Show Operator Options',
        description     = 'Shows additional operator options',
        default         = False,
    )

    reset_emptyName : BoolProperty( name = 'Reset "Empty Name"', default = False )
    emptyName : StringProperty (
        name        = 'Empty Name',
        default     = 'Group',
    )

    emptyLocation_List = [
        ("MEDIAN_POINT",             "Median",         "Median Point",          "", 0),
        ("BOUNDING_BOX_CENTER",      "Bound",          "Bounding Box Center",   "", 1),
        ("ACTIVE_ELEMENT",           "Active",         "Active Element",        "", 2),
        ("WORIGIN",                  "Origin",         "World Orign",           "", 3),
    ]

    reset_emptyLocation : BoolProperty( name = 'Reset "Empty Location"', default = False )
    emptyLocation : EnumProperty (
        items       = emptyLocation_List,
        name        = "Empty Location",
        default     = "MEDIAN_POINT",
    )

    emptySize_List = [
        ("1",      "1",         "",   "", 0),
        ("0.01",   "0.01",      "",   "", 1),
        ("0.0001", "0.0001",    "",   "", 2),
    ]

    reset_emptySize : BoolProperty( name = 'Reset "Empty Size"', default = False )
    emptySize : EnumProperty (
        items       = emptySize_List,
        name        = "Empty Location",
        default     = "0.01",
    )

    @classmethod
    def poll(cls, context):
        return (context.mode == "OBJECT") and ( len(context.selected_objects) > 0 )

    def invoke(self, context, event):

        addonPrefs = context.preferences.addons[__package__].preferences
        scn = context.scene

        # BEGIN Retreive Operator properties from addon preferences
        opPropNameList = ['emptyName', 'emptyLocation', 'emptySize']
        miscFunc.retreive_op_props_from_addonPrefs(self, context, addonPrefs=addonPrefs, opPropNameList=opPropNameList, opPropPrefix='groupWithEmpty_')
        # END Retreive Operator properties from addon preferences
        
        #EXPAND properties?
        if addonPrefs.groupWithEmpty_showOperatorOptions == 'EXPAND':
            self.showOperatorOptions = True
        else:
            self.showOperatorOptions = False

        #store cursor location at begin
        self.cursorLocAtBegin = mathutils.Vector(scn.cursor.location)
        
        #store mode at begin
        self.modeAtBegin = context.mode

        #store names of selected objects at begin
        self.selObjNames.clear()
        for obj in bpy.context.selected_objects:
            self.selObjNames.append(obj.name)

        return self.execute(context)
    # END invoke()

    def draw(self, context):
        lay = self.layout.column(align=True)

        if self.showOperatorOptions:
            box = lay.box().column(align=True)

            box.label(text="Empty Location:")
            emptyLocation_row = box.row(align=True)
            emptyLocation_row.prop(self, "emptyLocation", expand=True)
            emptyLocation_row.separator()
            resetBtn = emptyLocation_row.row(align=True)
            resetBtn.active = False
            resetBtn.prop(self, "reset_emptyLocation", toggle=True, text="", icon="LOOP_BACK", emboss=False)

            box.separator()

            box.label(text="Empty Size:")
            emptySize_row = box.row(align=True)
            emptySize_row.prop(self, "emptySize", expand=True)
            emptySize_row.separator()
            resetBtn = emptySize_row.row(align=True)
            resetBtn.active = False
            resetBtn.prop(self, "reset_emptySize", toggle=True, text="", icon="LOOP_BACK", emboss=False)

            lay.separator()


        emptyName_row = lay.row(align=True)
        emptyName_row.scale_y = 1.25
        emptyName_row.prop(self, "emptyName", text='')
        emptyName_row.separator()
        resetBtn = emptyName_row.row(align=True)
        resetBtn.active = False
        resetBtn.prop(self, "reset_emptyName", toggle=True, text="", icon="LOOP_BACK", emboss=False)

        emptyName_row.separator()

        if self.showOperatorOptions:
            icon = 'TRIA_UP'
        else:
            icon = 'TRIA_RIGHT'

        emptyName_row.prop(self, 'showOperatorOptions', text='', icon=icon)


    # END draw()

    def execute(self, context):
        
        # BEGIN Reset props if user clicked any "Reset" button in the operator adjustment panel at the lower left corner of the 3d viewport
        # ------------------------------------------------------------------------------------------------------------------------------------
        propsToReset = [
            ["reset_emptyName",         ["emptyName"]       ],
            ["reset_emptyLocation",     ["emptyLocation"]   ],
            ["reset_emptySize",         ["emptySize"]       ],
        ]

        miscFunc.resetOperatorProps(self, context, propsToReset)
        # ------------------------------------------------------------------------------------------------------------------------------------
        # END Reset props
        
        addonPrefs = context.preferences.addons[__package__].preferences
        scn = context.scene

        # BEGIN ensure one of the selected objects is an active object
        # ------------------------------------------------------------
        activeObj = context.view_layer.objects.active
        selObjs = context.selected_objects

        if len(selObjs) > 0:
            if (activeObj is None) or (not activeObj in selObjs):
                context.view_layer.objects.active = selObjs[0]
        # ------------------------------------------------------------
        # END ensure active obj



        #store name of active object
        self.activeObjName = f'{context.view_layer.objects.active.name}'


        #if mode is 'OBJECT'
        if self.modeAtBegin == 'OBJECT':

            #store name of current transform pivot location (e.g. MEDIAN_POINT, BOUNDING_BOX_CENTER, etc)
            tformPivotAtBegin = f'{scn.tool_settings.transform_pivot_point}'


            #if empty location is median or bounding, change "transform pivot" to median or bounding, snap cursor, then reset "transform pivot"
            if self.emptyLocation in ['MEDIAN_POINT', 'BOUNDING_BOX_CENTER', 'ACTIVE_ELEMENT']:
                scn.tool_settings.transform_pivot_point = self.emptyLocation #set pivot transform to emptyLocation

                if self.emptyLocation in ['MEDIAN_POINT', 'BOUNDING_BOX_CENTER']:
                    bpy.ops.view3d.snap_cursor_to_selected() #snap cursor to selected

                elif self.emptyLocation == 'ACTIVE_ELEMENT':
                    bpy.ops.view3d.snap_cursor_to_active() #snap cursor to active

                scn.tool_settings.transform_pivot_point = tformPivotAtBegin #reset pivot transform

            

            #create empty with custom name and link it to the scene
            newEmpty = bpy.data.objects.new(self.emptyName, None)
            bpy.context.collection.objects.link(newEmpty)

            #set location of the newly created empty according to the "emptyLocation" operator property
            if self.emptyLocation in ['MEDIAN_POINT', 'BOUNDING_BOX_CENTER', 'ACTIVE_ELEMENT']:
                newEmpty.location = scn.cursor.location

            elif self.emptyLocation == 'WORIGIN':
                newEmpty.location = (0,0,0)
            
            #set empty display size
            newEmpty.empty_display_size = float(self.emptySize)

            #store the parent of the active object if one exists
            childObjLastParent = context.view_layer.objects.active.parent

            #clear parents and keep transform of all of the selected objects
            bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')

            #parent the objects to the newly created empty
            for childObjName in self.selObjNames:
                childObj = bpy.data.objects[childObjName]

                childObj.parent = newEmpty
                childObj.matrix_parent_inverse = newEmpty.matrix_world.inverted()
            
            #if the active object at begin had a parent, then the newly created empty must be parented to its old parent
            if childObjLastParent is not None:
                newEmpty.parent = childObjLastParent
                newEmpty.matrix_parent_inverse = childObjLastParent.matrix_world.inverted()

            '''
            # NOTE: CURRENTLY BROKEN/NOT WORKING AND DON'T KNOW WHY.
            #    Find and Expand selected objects in each outliner
            # -----------------------------------------------------------------------------

            # expand outliner
            activeObj = context.view_layer.objects.active
            miscFunc.expand_selected_objs_in_outliner(self, context, selObjs=[activeObj], activeObj=activeObj)
            '''
            
            #reset cursor location
            scn.cursor.location = self.cursorLocAtBegin

        else:
            self.report({'INFO'}, 'Unsupported mode detected.  Please use "Object Mode".' )
        

        return {'FINISHED'}
    # END execute()


# END Operator()
Exemple #11
0
class SvGetAssetProperties(bpy.types.Node, SverchCustomTreeNode):
    ''' Get Asset Props '''
    bl_idname = 'SvGetAssetProperties'
    bl_label = 'Object ID Selector'
    bl_icon = 'OUTLINER_OB_EMPTY'

    def pre_updateNode(self, context):
        ''' must rebuild for each update'''
        self.type_collection_name.clear()
        for o in bpy.data.objects:
            if o.type == self.Type:
                self.type_collection_name.add().name = o.name

        # updateNode(self, context)
        self.process()

    def frame_updateNode(self, context):
        ''' must rebuild for each update'''
        self.frame_collection_name.clear()
        gp_layer = bpy.data.grease_pencil[self.gp_name].layers[self.gp_layer]
        for idx, f in enumerate(gp_layer.frames):
            self.frame_collection_name.add().name = str(idx) + ' | ' + str(
                f.frame_number)

        # updateNode(self, context)
        if self.gp_selected_frame_mode == 'active frame':
            if len(self.inputs) == 0:
                self.inputs.new("StringsSocket", 'frame#')
        else:
            if len(self.inputs) > 0:
                self.inputs.remove(self.inputs[-1])

        self.process()

    type_collection_name: bpy.props.CollectionProperty(
        type=bpy.types.PropertyGroup)
    frame_collection_name: bpy.props.CollectionProperty(
        type=bpy.types.PropertyGroup)

    M = [
        'actions', 'brushes', 'filepath', 'grease_pencil', 'groups', 'images',
        'libraries', 'linestyles', 'masks', 'materials', 'movieclips',
        'node_groups', 'particles', 'scenes', 'screens', 'shape_keys',
        'sounds', 'speakers', 'texts', 'textures', 'worlds', 'objects'
    ]
    T = [
        'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'ARMATURE', 'LATTICE',
        'EMPTY', 'CAMERA', 'LAMP', 'SPEAKER'
    ]

    Mode: EnumProperty(name="getmodes",
                       default="objects",
                       items=e(M),
                       update=updateNode)
    Type: EnumProperty(name="getmodes",
                       default="MESH",
                       items=e(T),
                       update=pre_updateNode)
    text_name: bpy.props.StringProperty(update=updateNode)
    object_name: bpy.props.StringProperty(update=updateNode)
    image_name: bpy.props.StringProperty(update=updateNode)
    pass_pixels: bpy.props.BoolProperty(update=updateNode)

    # GP props
    gp_name: bpy.props.StringProperty(update=updateNode)
    gp_layer: bpy.props.StringProperty(update=updateNode)
    gp_frame_current: bpy.props.BoolProperty(default=True, update=updateNode)
    gp_frame_override: bpy.props.IntProperty(default=1, update=updateNode)
    gp_stroke_idx: bpy.props.IntProperty(update=updateNode)

    gp_frame_mode_options = [
        (k, k, '', i) for i, k in enumerate(["pick frame", "active frame"])
    ]
    gp_selected_frame_mode: bpy.props.EnumProperty(
        items=gp_frame_mode_options,
        description="offers choice between current frame or available frames",
        default="pick frame",
        update=frame_updateNode)
    gp_frame_pick: bpy.props.StringProperty(update=frame_updateNode)
    gp_pass_points: bpy.props.BoolProperty(default=True, update=updateNode)

    def draw_gp_options(self, context, layout):
        # -- points  [p.co for p in points]
        # -- color
        #     -- color.color (stroke_color)
        #     -- color.fill_color
        #     -- color.file_alpha
        # -- line_width
        # -- draw_cyclic
        #  --- / triangles (only set is useful...)

        layout.prop_search(self,
                           'gp_name',
                           bpy.data,
                           'grease_pencil',
                           text='name')
        if not self.gp_name:
            return

        layout.prop_search(self,
                           'gp_layer',
                           bpy.data.grease_pencil[self.gp_name],
                           'layers',
                           text='layer')
        if not self.gp_layer:
            return

        layout.prop(self, 'gp_selected_frame_mode', expand=True)
        gp_layer = bpy.data.grease_pencil[self.gp_name].layers[self.gp_layer]
        frame_data = None
        if self.gp_selected_frame_mode == 'active frame':
            frame_data = gp_layer.active_frame
        else:
            # maybe display uilist with frame_index and frame_nmber.
            layout.prop_search(self, 'gp_frame_pick', self,
                               'frame_collection_name')
        layout.prop(self, 'gp_pass_points', text='pass points')

    def draw_buttons(self, context, layout):
        # layout.operator('node.'   ,text='refresh from scene')

        layout.row().prop(self, "Mode", text="data")

        if self.Mode == 'objects':
            layout.prop(self, "Type", "type")
            layout.prop_search(self,
                               'object_name',
                               self,
                               'type_collection_name',
                               text='name',
                               icon='OBJECT_DATA')
        elif self.Mode == 'texts':
            layout.prop_search(self,
                               'text_name',
                               bpy.data,
                               'texts',
                               text='name')
        elif self.Mode == 'images':
            layout.prop_search(self,
                               'image_name',
                               bpy.data,
                               'images',
                               text='name')
            if self.image_name:
                layout.prop(self, 'pass_pixels', text='pixels')
                # size ?  new socket outputting [w/h]
        elif self.Mode == 'grease_pencil':
            self.draw_gp_options(context, layout)

    def sv_init(self, context):
        self.outputs.new('StringsSocket', "Objects")
        self.width = 210
        self.Type = 'MESH'  # helps init the custom object prop_search

    def process(self):
        output_socket = self.outputs['Objects']
        if not output_socket.is_linked:
            return

        data_list = getattr(bpy.data, self.Mode)

        if self.Mode == 'objects':
            if self.object_name:
                output_socket.sv_set([data_list[self.object_name]])
            else:
                output_socket.sv_set(
                    [i for i in data_list if i.type == self.Type])

        elif self.Mode == 'texts':
            if self.text_name:
                output_socket.sv_set([[data_list[self.text_name].as_string()]])
            else:
                output_socket.sv_set(data_list[:])

        elif self.Mode == 'images':
            if self.image_name:
                img = data_list[self.image_name]
                if self.pass_pixels:
                    output_socket.sv_set([[img.pixels[:]]])
                else:
                    output_socket.sv_set([img])
            else:
                output_socket.sv_set(data_list[:])

        elif self.Mode == 'grease_pencil':
            # candidate for refactor
            if self.gp_name and self.gp_layer:
                GP_and_layer = data_list[self.gp_name].layers[self.gp_layer]
                if self.gp_selected_frame_mode == 'active frame':
                    if len(self.inputs) > 0 and self.inputs[0].is_linked:

                        frame_number = self.inputs[0].sv_get()[0][0]
                        key = frame_from_available2(frame_number, GP_and_layer)
                        strokes = GP_and_layer.frames[key].strokes
                    else:
                        strokes = GP_and_layer.active_frame.strokes

                    if not strokes:
                        return

                    if self.gp_pass_points:
                        output_socket.sv_set([[p.co[:] for p in s.points]
                                              for s in strokes])
                    else:
                        output_socket.sv_set(strokes)
                else:
                    if self.gp_frame_pick:
                        idx_from_frame_pick = int(
                            self.gp_frame_pick.split(' | ')[0])
                        frame_data = GP_and_layer.frames[idx_from_frame_pick]
                        if frame_data:
                            if self.gp_pass_points:
                                output_socket.sv_set(
                                    [[p.co[:] for p in s.points]
                                     for s in frame_data.strokes])
                            else:
                                output_socket.sv_set(strokes)

        else:
            output_socket.sv_set(data_list[:])
Exemple #12
0
    x = bbox['xmin'] + (bbox['xmax'] - bbox['xmin']) / 2
    y = bbox['ymin'] + (bbox['ymax'] - bbox['ymin']) / 2
    z = bbox['zmin'] + (bbox['zmax'] - bbox['zmin']) / 2
    return (x, y, z)


def getBBoxDim(bbox):
    dx = bbox['xmax'] - bbox['xmin']
    dy = bbox['ymax'] - bbox['ymin']
    dz = bbox['zmax'] - bbox['zmin']
    return (dx, dy, dz)


#store property in scene
bpy.types.Scene.objLst = EnumProperty(attr="obj_list",
                                      name="Object",
                                      description="Choose an object",
                                      items=listObjects)
bpy.types.Scene.camLst = EnumProperty(attr="cam_list",
                                      name="Camera",
                                      description="Choose a camera",
                                      items=listCams)


class ToolsPanelSetGeorefCam(bpy.types.Panel):
    bl_category = "GIS"
    bl_label = "Georef cam"
    bl_space_type = "VIEW_3D"
    bl_context = "objectmode"
    bl_region_type = "TOOLS"

    def draw(self, context):
class VelvetRevolver(bpy.types.Operator, ExportHelper):
    """Mass create proxies and/or intra-frame copies from original files"""
    bl_idname = "export.revolver"
    bl_label = "Export to Revolver"
    filename_ext = ".revolver"

    transcode_items = (
        ('is_prores', 'ProRes422', ''),
        ('is_mjpeg', 'MJPEG', '')
    )

    proxies = BoolProperty(
        name="360p proxies",
        description="Create 640x368 proxies with same FPS as current scene",
        default=False,
    )
    copies = BoolProperty(
        name="Full-res copies in intra-frame codec",
        description="Create full-res copies with same FPS as current scene (slow)",
        default=False,
    )
    v_format = EnumProperty(
        name="Format",
        default="is_prores",
        description="Intra-frame format for the creation of proxies and/or copies",
        items=transcode_items
    )
    prop_ar = IntProperty(
        name="Audio Sample Rate",
        description="Transcoded videos will have this audio rate",
        default=48000
    )
    prop_deint = BoolProperty(
        name="Deinterlace videos",
        description="Uses FFMPEG Yadif filter to deinterlace all videos",
        default=False,
    )
    prop_ac = BoolProperty(
        name="Force mono audio?",
        description="Forces FFMPEG to transcode videos to mono - easier panning in Blender, but usually not recommended",
        default=False,
    )

    def draw(self, context):
        render = context.scene.render
        fps = render.fps / render.fps_base

        layout = self.layout
        box = layout.box()
        box.label('What to do in selected folder? Create...')
        box.prop(self, 'proxies')
        box.prop(self, 'copies')
        box.label('Proxies and/or copies should be in:')
        box.prop(self, 'v_format')
        box.label("Resulting videos will have %.2f FPS." % fps)
        box.label("You can change the FPS at Properties.")

        box = layout.box()
        box.label('Properties for videos:')
        box.prop(self, 'prop_ar')
        box.prop(self, 'prop_deint')
        box.prop(self, 'prop_ac')

    @classmethod
    def poll(cls, context):
        if bpy.data.scenes:
            return bpy.data.scenes is not None

    def execute(self, context):
        preferences = bpy.context.user_preferences
        ffCommand = preferences.addons['velvet_revolver'].preferences.ffCommand

        videosFolderPath, blenderFile = os.path.split(self.filepath)
        videosFolderPath += os.sep

        render = context.scene.render
        fps = render.fps / render.fps_base

        sources = []
        for i in glob.glob(videosFolderPath + "*.*"):
            if i[-4:].lower() in bpy.path.extensions_movie:
                # The line below does not allow for the creation of proxies from
                # a _PRORES or _MJPEG file. TO-DO: creation of sources = [] has 
                # to be inside self.proxies and self.copies. Then, the script
                # should check for a "original" file (ie. without _prores) ->
                # if it finds it, pass; else, execute ffmpeg command.
                # Also: 'sources' should be sorted by filesize, so that
                # smaller files are transcoded first (create this as an option:
                # sort by filesize, sort by name).
                if "_proxy." not in i and "_MJPEG." not in i and "_PRORES." not in i:
                    sources.append(i)

        if self.proxies:
            for source in sources:
                v_res = "proxy"
                vs = VideoSource(ffCommand, videosFolderPath, source, v_res,
                                 self.v_format, fps, self.prop_deint,
                                 self.prop_ar, self.prop_ac)
                vs.runFF()

        if self.copies:
            for source in sources:
                v_res = "fullres"
                vs = VideoSource(ffCommand, videosFolderPath, source, v_res,
                                 self.v_format, fps, self.prop_deint,
                                 self.prop_ar, self.prop_ac)
                vs.runFF()

        if not self.proxies and not self.copies:
            print("No action selected for Velvet Revolver. Aborting.")

        return {'FINISHED'}
Exemple #14
0
class RPR_DenoiserProperties(RPR_Properties):
    """ Denoiser properties. This is a child property in RPR_ViewLayerProperties """
    enable: BoolProperty(
        description="Enable RPR Denoiser",
        default=False,
    )

    # only enable ML denoiser on windows
    items = (('BILATERAL', "Bilateral", "Bilateral", 0),
             ('LWR', "Local Weighted Regression", "Local Weighted Regression",
              1), ('EAW', "Edge Avoiding Wavelets", "Edge Avoiding Wavelets",
                   2), ('ML', "Machine Learning", "Machine Learning", 3))

    filter_type: EnumProperty(name="Filter Type",
                              items=items,
                              description="Filter type",
                              default='ML')

    scale_by_iterations: BoolProperty(
        name="Scale Denoising Iterations",
        description="Scale the amount of denoiser blur by number of iterations. "
        "This will give more blur for renders with less samples, "
        "and become sharper as more samples are added.",
        default=True)

    # bilateral props
    radius: IntProperty(name="Radius",
                        description="Radius",
                        min=1,
                        max=50,
                        default=1)
    p_sigma: FloatProperty(
        name="Position Sigma",
        description="Threshold for detecting position differences",
        min=0.0,
        soft_max=1.0,
        default=.1)

    # EAW props
    color_sigma: FloatProperty(
        name="Color Sigma",
        description="Threshold for detecting color differences",
        min=0.0,
        soft_max=1.0,
        default=.75)
    normal_sigma: FloatProperty(
        name="Normal Sigma",
        description="Threshold for detecting normal differences",
        min=0.0,
        soft_max=1.0,
        default=.01)
    depth_sigma: FloatProperty(
        name="Depth Sigma",
        description="Threshold for detecting z depth differences",
        min=0.0,
        soft_max=1.0,
        default=.01)
    trans_sigma: FloatProperty(
        name="ID Sigma",
        description="Threshold for detecting Object ID differences",
        min=0.0,
        soft_max=1.0,
        default=.01)

    # LWR props
    samples: IntProperty(
        name="Samples",
        description=
        "Number of samples used, more will give better results while being longer",
        min=2,
        soft_max=10,
        max=100,
        default=4)
    half_window: IntProperty(name="Filter radius",
                             description="The radius of pixels to sample from",
                             min=1,
                             soft_max=10,
                             max=100,
                             default=4)
    bandwidth: FloatProperty(
        name="Bandwidth",
        description=
        "Bandwidth of the filter, a samller value gives less noise, but may filter image detail",
        min=0.1,
        max=1.0,
        default=.2)

    # ML props
    ml_color_only: BoolProperty(
        name="Use Color AOV only",
        description=
        "Use Color AOV only instead of using additional required AOVs",
        default=True if not utils.IS_MAC else False)
    ml_use_fp16_compute_type: BoolProperty(
        name="Use 16-bit Compute",
        description=
        "Reduce precision to 16 bit. It uses less memory and increases denoising speed, but with less quality.\n"
        "Available only for viewport render.",
        default=False)

    def get_settings(self, scene, is_final_engine=True):
        return {
            'enable': self.enable
            and self.is_available(scene, is_final_engine),
            'filter_type': self.filter_type,
            'color_sigma': self.color_sigma,
            'normal_sigma': self.normal_sigma,
            'p_sigma': self.p_sigma,
            'depth_sigma': self.depth_sigma,
            'trans_sigma': self.trans_sigma,
            'radius': self.radius,
            'samples': self.samples,
            'half_window': self.half_window,
            'bandwidth': self.bandwidth,
            'ml_color_only': self.ml_color_only,
            'ml_use_fp16_compute_type': self.ml_use_fp16_compute_type,
        }

    def is_available(self, scene, is_final_engine=True):
        return scene.rpr.render_quality != 'FULL2'
Exemple #15
0
class SvMatrixMathNode(bpy.types.Node, SverchCustomTreeNode):
    ''' Math operation on matrices '''
    bl_idname = 'SvMatrixMathNode'
    bl_label = 'Matrix Math'
    bl_icon = 'OUTLINER_OB_EMPTY'

    def update_operation(self, context):
        self.label = "Matrix " + self.operation.title()
        self.update_sockets()
        updateNode(self, context)

    prePost = EnumProperty(
        name='Pre Post',
        description='Order of operations PRE = A op B vs POST = B op A)',
        items=prePostItems,
        default="PRE",
        update=updateNode)

    operation = EnumProperty(
        name="Operation",
        description="Operation to apply on the given matrices",
        items=operationItems,
        default="MULTIPLY",
        update=update_operation)

    filter_t = BoolProperty(
        name="Filter Translation",
        description="Filter out the translation component of the matrix",
        default=False,
        update=updateNode)

    filter_r = BoolProperty(
        name="Filter Rotation",
        description="Filter out the rotation component of the matrix",
        default=False,
        update=updateNode)

    filter_s = BoolProperty(
        name="Filter Scale",
        description="Filter out the scale component of the matrix",
        default=False,
        update=updateNode)

    def sv_init(self, context):
        self.inputs.new('MatrixSocket', "A", "A")
        self.inputs.new('MatrixSocket', "B", "B")

        self.outputs.new('MatrixSocket', "C", "C")

        self.operation = "MULTIPLY"

    def update_sockets(self):
        inputs = self.inputs

        if self.operation == "MULTIPLY":
            # two matrix inputs available
            if not "B" in inputs:
                inputs.new("MatrixSocket", "B")
        else:
            # one matrix input available
            if "B" in inputs:
                inputs.remove(inputs["B"])

    def draw_buttons(self, context, layout):
        layout.prop(self, "operation", text="")
        if self.operation == "MULTIPLY":
            layout.prop(self, "prePost", expand=True)
        elif self.operation == "FILTER":
            row = layout.row(align=True)
            row.prop(self, "filter_t", toggle=True, text="T")
            row.prop(self, "filter_r", toggle=True, text="R")
            row.prop(self, "filter_s", toggle=True, text="S")

    def operation_filter(self, a):
        T, R, S = a.decompose()

        if self.filter_t:
            mat_t = Matrix().Identity(4)
        else:
            mat_t = Matrix().Translation(T)

        if self.filter_r:
            mat_r = Matrix().Identity(4)
        else:
            mat_r = R.to_matrix().to_4x4()

        if self.filter_s:
            mat_s = Matrix().Identity(4)
        else:
            mat_s = Matrix().Identity(4)
            mat_s[0][0] = S[0]
            mat_s[1][1] = S[1]
            mat_s[2][2] = S[2]

        m = mat_t * mat_r * mat_s

        return m

    def get_operation(self):
        if self.operation == "MULTIPLY":
            return lambda a, b: a * b
        elif self.operation == "FILTER":
            return self.operation_filter
        elif self.operation == "INVERT":
            return lambda a: a.inverted()

    def process(self):
        outputs = self.outputs
        if not outputs['C'].is_linked:
            return

        inputs = self.inputs
        id_mat = Matrix_listing([Matrix.Identity(4)])
        A = Matrix_generate(inputs['A'].sv_get(default=id_mat))

        if self.operation in {"MULTIPLY"}:
            # two matrix inputs available
            B = Matrix_generate(inputs['B'].sv_get(default=id_mat))

            if self.prePost == "PRE":
                parameters = match_long_repeat([A, B])
            else:
                parameters = match_long_repeat([B, A])
        else:
            # one matrix input available
            parameters = [A]

        operation = self.get_operation()

        matrixList = []
        for params in zip(*parameters):
            c = operation(*params)
            matrixList.append(c)

        matrices = Matrix_listing(matrixList)

        outputs['C'].sv_set(matrices)
Exemple #16
0
class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator):
    """
    Operation class: Paste UV coordinate by selection sequence
    """

    bl_idname = "uv.muv_copy_paste_uv_selseq_paste_uv"
    bl_label = "Paste UV (Selection Sequence)"
    bl_description = "Paste UV coordinate by selection sequence"
    bl_options = {'REGISTER', 'UNDO'}

    uv_map = StringProperty(default="__default", options={'HIDDEN'})
    strategy = EnumProperty(
        name="Strategy",
        description="Paste Strategy",
        items=[('N_N', 'N:N', 'Number of faces must be equal to source'),
               ('N_M', 'N:M', 'Number of faces must not be equal to source')],
        default="N_M")
    flip_copied_uv = BoolProperty(name="Flip Copied UV",
                                  description="Flip Copied UV...",
                                  default=False)
    rotate_copied_uv = IntProperty(default=0,
                                   name="Rotate Copied UV",
                                   min=0,
                                   max=30)
    copy_seams = BoolProperty(name="Seams",
                              description="Copy Seams",
                              default=True)

    @classmethod
    def poll(cls, context):
        # we can not get area/space/region from console
        if common.is_console_mode():
            return True
        sc = context.scene
        props = sc.muv_props.copy_paste_uv_selseq
        if not props.src_info:
            return False
        return _is_valid_context(context)

    def execute(self, context):
        props = context.scene.muv_props.copy_paste_uv_selseq
        if not props.src_info:
            self.report({'WARNING'}, "Need copy UV at first")
            return {'CANCELLED'}

        objs = common.get_uv_editable_objects(context)
        # poll() method ensures that only one object is selected.
        obj = objs[0]
        bm = common.create_bmesh(obj)

        # get UV layer
        uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info,
                                        self.uv_map)
        if not uv_layers:
            return {'CANCELLED'}

        # get selected face
        dest_info = _get_select_history_dest_face_info(self, bm, uv_layers,
                                                       props.src_info,
                                                       self.strategy)
        if dest_info is None:
            return {'CANCELLED'}

        # paste
        ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers,
                       self.strategy, self.flip_copied_uv,
                       self.rotate_copied_uv, self.copy_seams)
        if ret:
            return {'CANCELLED'}

        face_count = len(props.src_info[list(dest_info.keys())[0]])
        self.report({'INFO'}, "{} face(s) are pasted".format(face_count))

        bmesh.update_edit_mesh(obj.data)

        if compat.check_version(2, 80, 0) < 0:
            if self.copy_seams is True:
                obj.data.show_edge_seams = True

        return {'FINISHED'}
Exemple #17
0
from bpy.types import Operator
from bpy_extras.io_utils import ImportHelper, ExportHelper
from bpy.props import (
    StringProperty,
    BoolProperty,
    EnumProperty,
    FloatProperty,
)

# property shared by both operators
rotation_order = EnumProperty(name="Rotation order",
                              description="Choose the export rotation order",
                              items=(
                                  ('XYZ', "XYZ", "XYZ"),
                                  ('XZY', "XZY", "XZY"),
                                  ('YXZ', "YXZ", "YXZ"),
                                  ('YZX', "YZX", "YZX"),
                                  ('ZXY', "ZXY", "ZXY"),
                                  ('ZYX', "ZYX", "ZYX"),
                              ),
                              default='XYZ')


class ImportChan(Operator, ImportHelper):
    """Import animation from .chan file, exported from nuke or houdini """ \
    """(the importer uses frame numbers from the file)"""
    bl_idname = "import_scene.import_chan"
    bl_label = "Import chan file"

    filename_ext = ".chan"
Exemple #18
0
class LuxCoreWorldProps(bpy.types.PropertyGroup):
    use_cycles_settings: BoolProperty(name="Use Cycles Settings",
                                      default=False)

    lights = [
        ("sky2", "Sky", "Hosek and Wilkie sky model", 0),
        ("infinite", "HDRI", "High dynamic range image", 1),
        ("constantinfinite", "Flat Color", "Flat background color", 2),
        ("none", "None", "No background light", 3),
    ]
    light: EnumProperty(name="Background", items=lights, default="sky2")

    # Generic properties shared by all background light types
    gain: FloatProperty(name="Gain",
                        default=1,
                        min=0,
                        precision=4,
                        description="Brightness multiplier")
    exposure: FloatProperty(name="Exposure",
                            default=0,
                            soft_min=-10,
                            soft_max=10,
                            precision=2,
                            description=EXPOSURE_DESCRIPTION)
    rgb_gain: FloatVectorProperty(name="Tint",
                                  default=(1, 1, 1),
                                  min=0,
                                  max=1,
                                  subtype="COLOR",
                                  description=RGB_GAIN_DESC)
    importance: FloatProperty(name="Importance",
                              default=1,
                              min=0,
                              description=IMPORTANCE_DESCRIPTION)
    lightgroup: StringProperty(name="Light Group", description=LIGHTGROUP_DESC)

    # sky2 settings
    def poll_sun(self, obj):
        return obj.type == "LIGHT" and obj.data and obj.data.type == "SUN"

    sun: PointerProperty(
        name="Sun",
        type=bpy.types.Object,
        poll=poll_sun,  # The poll method filters the objects in the scene
        description=SUN_DESC)
    sun_sky_gain: FloatProperty(name="Gain",
                                default=0.00002,
                                min=0,
                                precision=6,
                                step=0.00001,
                                description=SUN_SKY_GAIN_DESC)
    # Only shown in UI when light is sky2 and a sun is attached
    use_sun_gain_for_sky: BoolProperty(name="Use Sun Gain",
                                       default=True,
                                       description=USE_SUN_GAIN_FOR_SKY_DESC)
    turbidity: FloatProperty(name="Turbidity",
                             default=2.2,
                             min=0,
                             max=30,
                             description=TURBIDITY_DESC)
    groundalbedo: FloatVectorProperty(name="Ground Albedo",
                                      default=(0.5, 0.5, 0.5),
                                      min=0,
                                      max=1,
                                      subtype="COLOR",
                                      description=GROUND_ALBEDO_DESC)
    ground_enable: BoolProperty(name="Use Ground Color",
                                default=False,
                                description=GROUND_ENABLE_DESC)
    ground_color: FloatVectorProperty(name="Ground Color",
                                      default=(0.5, 0.5, 0.5),
                                      min=0,
                                      max=1,
                                      subtype="COLOR",
                                      description=GROUND_COLOR_DESC)

    # infinite
    def update_image(self, context):
        self.image_user.update(self.image)

    image: PointerProperty(name="Image",
                           type=bpy.types.Image,
                           update=update_image)
    image_user: PointerProperty(type=LuxCoreImageUser)
    gamma: FloatProperty(name="Gamma",
                         default=1,
                         min=0,
                         description=GAMMA_DESCRIPTION)
    sampleupperhemisphereonly: BoolProperty(
        name="Sample Upper Hemisphere Only",
        default=False,
        description=SAMPLEUPPERHEMISPHEREONLY_DESCRIPTION)
    rotation: FloatProperty(name="Z Axis Rotation",
                            default=0,
                            subtype="ANGLE",
                            unit="ROTATION")

    # sky2, sun, infinite, constantinfinite
    visibility_indirect_diffuse: BoolProperty(
        name="Diffuse", default=True, description=VIS_INDIRECT_DIFFUSE_DESC)
    visibility_indirect_glossy: BoolProperty(
        name="Glossy", default=True, description=VIS_INDIRECT_GLOSSY_DESC)
    visibility_indirect_specular: BoolProperty(
        name="Specular", default=True, description=VIS_INDIRECT_SPECULAR_DESC)

    # sky2, infinite, constantinfinite
    use_envlight_cache: BoolProperty(name="Use Env. Light Cache",
                                     default=True,
                                     description=ENVLIGHT_CACHE_DESC)

    volume: PointerProperty(type=bpy.types.NodeTree)

    @classmethod
    def register(cls):
        bpy.types.World.luxcore = PointerProperty(
            name="LuxCore World Settings",
            description="LuxCore World settings",
            type=cls,
        )

    @classmethod
    def unregister(cls):
        del bpy.types.World.luxcore
Exemple #19
0
class BtoASceneSettings(bpy.types.PropertyGroup):
    name="BtoASceneSettings"
    # Sampling
    AA_samples = IntProperty(name="Global Samples",
                             description="Number of samples per pixel",
                             min=-10, max=32, default=2)
    AA_pattern = EnumProperty(items=(("0","regular",""),
                             ("1","random",""),
                             ("2","jittered",""),
                             ("3","multi_jittered",""),
                             ("4","poisson_bc",""),
                             ("5","dithered",""),
                             ("6","nrooks",""),
                             ("7","schlick","")), 
                             name="Pattern", description="AA pattern", default="3")
    AA_seed = IntProperty(name="Seed", description="Seed for samples",
                          min=-1000, max=1000, default=1)
    AA_motionblur_pattern = EnumProperty(items=(("0","regular",""),
                             ("1","random",""),
                             ("2","jittered",""),
                             ("3","multi_jittered",""),
                             ("4","poisson_bc",""),
                             ("5","dithered",""),
                             ("6","nrooks",""),
                             ("7","schlick","")), 
                             name="MBlur Pattern", description="Motionblur pattern",
                             default="2")
    AA_sample_clamp = FloatProperty(name="Sample Clamp",
                                    description="Clamp distance",default=1e+30)
    AA_clamp_affect_aovs = BoolProperty(name="Clamp AOVs", 
                                        description="Clamp affects AOVs")
    AA_sampling_dither = IntProperty(name="Dither", description="Dither for samples",
                                     min=0, max=100, default=4)
    # GI Settings
    GI_diffuse_samples = IntProperty(name="GI Diffuse",
                                     description="Number of samples for GI diffuse",
                                     min=0, max=32, default=2)
    GI_glossy_samples = IntProperty(name="GI Glossy",
                                    description="Number of samples for GI glossy",
                                    min=0, max=32, default=2)
    GI_diffuse_depth = IntProperty(name="Diffuse Depth",
                                description="Number of bounces for GI diffuse",
                                min=0, max=32, default=2)
    GI_glossy_depth = IntProperty(name="Glossy Depth",
                                description="Number of bounces for glossy",
                                 min=0, max=32, default=2)
    GI_reflection_depth = IntProperty(name="Reflection",
                                    description="Number of bounces for reflection",
                                    min=0, max=32, default=2)
    GI_refraction_depth = IntProperty(name="Refraction",
                                    description="Number of bounces for refraction",
                                    min=0, max=32, default=2)
    # interactive settings
    progressive_min = IntProperty(name="Progressive Min",
                                description="lowest sample for progressive",
                                min=-20, max=0, default=-3)
    enable_progressive = BoolProperty(name="Progressive", 
                                    description="Enable Progressive Rendering",
                                    default=True)
    bucket_size = IntProperty(name="Bucket Size",
                            description="Size of buckets",
                            min=8, max=1024, default=64)
    bucket_scanning = EnumProperty(items=(("-1","Bucket Scan",""),
                                ("0","top",""),
                                ("1","bottom",""),
                                ("2","letf",""),
                                ("3","right",""),
                                ("4","random",""),
                                ("5","woven",""),
                                ("6","spiral",""),
                                ("7","hilbert","")), 
                                name="", description="bucket scanning",
                                default="0")
Exemple #20
0
class LuxCoreImageUser(PropertyGroup):
    """
    We can't use Blender's ImageUser class, so we have to create our own.
    The ImageUser contains information about how an image is used by a datablock.
    For example, the same image sequence can be used with offset 5 by a pointlight
    and with offset 2 by an imagemap node, so the pointlight and the imagemap node
    each have their own ImageUser instance that saves this information.
    """

    # This image reference is just for internal bookkeeping
    image = PointerProperty(type=bpy.types.Image)

    first_frame = IntProperty(name="First Frame",
                              default=1,
                              min=1,
                              description=FIRST_FRAME_DESC)
    last_frame = IntProperty(name="Last Frame",
                             default=2,
                             min=1,
                             description=LAST_FRAME_DESC)
    # TODO description?
    frame_offset = IntProperty(name="Offset", default=0)

    pick_random = BoolProperty(
        name="Pick Random",
        default=False,
        description=
        "Pick a random frame n so that: First Frame <= n <= Last Frame")
    seed = IntProperty(name="Seed", default=0, description=SEED_DESC)
    reverse = BoolProperty(
        name="Reverse",
        default=False,
        description="Reverse the number of frames in the sequence")

    wrap_modes = [
        ("none", "None", WRAP_NONE_DESC, 0),
        ("clamp", "Clamp",
         "Use the first (or last) frame of the sequence: 111 12345 555", 1),
        ("repeat", "Repeat", "Loop the sequence: 12345 12345 12345", 2),
        ("pingpong", "Ping-Pong",
         "Like repeat, but every second repetition is reversed: 12345 54321 12345",
         3),
    ]
    wrap_mode = EnumProperty(
        name="Wrap",
        items=wrap_modes,
        default="clamp",
        description=
        "How to handle the case of being outside of the sequence range")

    def update(self, image):
        """ This function should be called on each image update """
        if image and self.image != image:
            # A new or different image was linked,
            # auto-detect sequence length and first frame offset
            if image.source == "SEQUENCE":
                indexed_filepaths = utils.image_sequence_resolve_all(image)

                if indexed_filepaths:
                    first_index, first_path = indexed_filepaths[0]
                    frame_count = len(indexed_filepaths)
                    self.first_frame = 1
                    self.last_frame = frame_count
                    self.frame_offset = -first_index + 1
        self.image = image

    def get_frame(self, scene):
        """
        Calculate the current frame in the sequence.
        Note that the frame numbering starts at 1 and ends at frame_count (see below).
        """
        frame = scene.frame_current + self.frame_offset

        if self.first_frame > self.last_frame:
            raise ValueError("First frame greater than last frame")

        if self.pick_random:
            random.seed(self.seed ^ frame)
            return random.randint(self.first_frame, self.last_frame)

        frame_count = self.last_frame - self.first_frame + 1

        if self.wrap_mode == "clamp":
            frame = utils.clamp(frame, self.first_frame, self.last_frame)
        elif self.wrap_mode == "repeat":
            frame -= 1
            frame %= frame_count
            frame += 1
            frame += self.first_frame - 1
        elif self.wrap_mode == "pingpong":
            # Ping-Pong is like repeat, but every second repetition is reversed.
            # Note that this means that the first and last frame appear to be doubled.
            # E.g. with 5 frames:
            # Repeat:    12345 12345 12345
            # Ping-Pong: 12345 54321 12345
            frame %= frame_count * 2
            temp = frame
            frame = frame_count - abs(frame - frame_count)

            if temp > frame_count:
                frame += 1

            frame += self.first_frame - 1
            frame = utils.clamp(frame, self.first_frame, self.last_frame)

        if self.reverse:
            # We have to undo the first frame offset before reversing, then add it again
            frame -= self.first_frame - 1
            frame = frame_count - frame + 1
            frame += self.first_frame - 1

        return frame

    def draw(self, layout, scene):
        if self.image and self.image.source == "SEQUENCE":
            box = layout.box()
            sub = box.column(align=True)

            try:
                frame = self.get_frame(scene)
                sub.label("Frame: %d" % frame)
                if frame < self.first_frame or frame > self.last_frame:
                    sub.label("Out of range", icon="ERROR")
            except ValueError as error:
                sub.label(str(error), icon="CANCEL")

            sub.prop(self, "first_frame")
            sub.prop(self, "last_frame")
            sub.prop(self, "frame_offset")

            sub.prop(self, "pick_random")
            if self.pick_random:
                sub.prop(self, "seed")

            sub2 = sub.column(align=True)
            sub2.active = not self.pick_random
            sub2.prop(self, "reverse")
            sub2.prop(self, "wrap_mode")
class ExportGLTF2_Base:
    # TODO: refactor to avoid boilerplate

    def __init__(self):
        self.is_draco_available = gltf2_io_draco_compression_extension.dll_exists()

    bl_options = {'UNDO', 'PRESET'}

    export_format = EnumProperty(
        name='Format',
        items=(('GLB', 'glTF Binary (.glb)',
                'Exports a single file, with all data packed in binary form. '
                'Most efficient and portable, but more difficult to edit later'),
               ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
                'Exports a single file, with all data packed in JSON. '
                'Less efficient than binary, but easier to edit later'),
               ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
                'Exports multiple files, with separate JSON, binary and texture data. '
                'Easiest to edit later')),
        description=(
            'Output format and embedding options. Binary is most efficient, '
            'but JSON (embedded or separate) may be easier to edit later'
        ),
        default='GLB'
    )

    ui_tab = EnumProperty(
        items=(('GENERAL', "General", "General settings"),
               ('MESHES', "Meshes", "Mesh settings"),
               ('OBJECTS', "Objects", "Object settings"),
               ('ANIMATION', "Animation", "Animation settings")),
        name="ui_tab",
        description="Export setting categories",
    )

    export_copyright = StringProperty(
        name='Copyright',
        description='Legal rights and conditions for the model',
        default=''
    )

    export_image_format = EnumProperty(
        name='Images',
        items=(('NAME', 'Automatic',
                'Determine the image format from the blender image name'),
                ('JPEG', 'JPEG Format (.jpg)',
                'Encode and save textures as .jpg files. Be aware of a possible loss in quality'),
               ('PNG', 'PNG Format (.png)',
                'Encode and save textures as .png files')
               ),
        description=(
            'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
            'applications due to the smaller file size'
        ),
        default='NAME'
    )

    export_texcoords = BoolProperty(
        name='UVs',
        description='Export UVs (texture coordinates) with meshes',
        default=True
    )

    export_normals = BoolProperty(
        name='Normals',
        description='Export vertex normals with meshes',
        default=True
    )

    export_draco_mesh_compression_enable = BoolProperty(
        name='Draco mesh compression',
        description='Compress mesh using Draco',
        default=False
    )

    export_draco_mesh_compression_level = IntProperty(
        name='Compression level',
        description='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
        default=6,
        min=0,
        max=6
    )

    export_draco_position_quantization = IntProperty(
        name='Position quantization bits',
        description='Quantization bits for position values (0 = no quantization)',
        default=14,
        min=0,
        max=30
    )

    export_draco_normal_quantization = IntProperty(
        name='Normal quantization bits',
        description='Quantization bits for normal values (0 = no quantization)',
        default=10,
        min=0,
        max=30
    )

    export_draco_texcoord_quantization = IntProperty(
        name='Texcoord quantization bits',
        description='Quantization bits for texture coordinate values (0 = no quantization)',
        default=12,
        min=0,
        max=30
    )

    export_tangents = BoolProperty(
        name='Tangents',
        description='Export vertex tangents with meshes',
        default=False
    )

    export_materials = BoolProperty(
        name='Materials',
        description='Export materials',
        default=True
    )

    export_colors = BoolProperty(
        name='Vertex Colors',
        description='Export vertex colors with meshes',
        default=True
    )

    export_cameras = BoolProperty(
        name='Cameras',
        description='Export cameras',
        default=False
    )

    export_selected = BoolProperty(
        name='Selected Objects',
        description='Export selected objects only',
        default=False
    )

    export_extras = BoolProperty(
        name='Custom Properties',
        description='Export custom properties as glTF extras',
        default=False
    )

    export_yup = BoolProperty(
        name='+Y Up',
        description='Export using glTF convention, +Y up',
        default=True
    )

    export_apply = BoolProperty(
        name='Apply Modifiers',
        description='Apply modifiers (excluding Armatures) to mesh objects',
        default=False
    )

    export_animations = BoolProperty(
        name='Animations',
        description='Exports active actions and NLA tracks as glTF animations',
        default=True
    )

    export_frame_range = BoolProperty(
        name='Limit to Playback Range',
        description='Clips animations to selected playback range',
        default=True
    )

    export_frame_step = IntProperty(
        name='Sampling Rate',
        description='How often to evaluate animated values (in frames)',
        default=1,
        min=1,
        max=120
    )

    export_force_sampling = BoolProperty(
        name='Always Sample Animations',
        description='Apply sampling to all animations',
        default=False
    )

    export_current_frame = BoolProperty(
        name='Use Current Frame',
        description='Export the scene in the current animation frame',
        default=False
    )

    export_skins = BoolProperty(
        name='Skinning',
        description='Export skinning (armature) data',
        default=True
    )

    export_all_influences = BoolProperty(
        name='Include All Bone Influences',
        description='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
        default=False
    )

    export_morph = BoolProperty(
        name='Shape Keys',
        description='Export shape keys (morph targets)',
        default=True
    )

    export_morph_normal = BoolProperty(
        name='Shape Key Normals',
        description='Export vertex normals with shape keys (morph targets)',
        default=True
    )

    export_morph_tangent = BoolProperty(
        name='Shape Key Tangents',
        description='Export vertex tangents with shape keys (morph targets)',
        default=False
    )

    export_lights = BoolProperty(
        name='Punctual Lights',
        description='Export directional, point, and spot lights. '
                    'Uses "KHR_lights_punctual" glTF extension',
        default=False
    )

    export_displacement = BoolProperty(
        name='Displacement Textures (EXPERIMENTAL)',
        description='EXPERIMENTAL: Export displacement textures. '
                    'Uses incomplete "KHR_materials_displacement" glTF extension',
        default=False
    )

    will_save_settings = BoolProperty(
        name='Remember Export Settings',
        description='Store glTF export settings in the Blender project',
        default=False)

    # Custom scene property for saving settings
    scene_key = "glTF2ExportSettings"

    #

    def invoke(self, context, event):
        settings = context.scene.get(self.scene_key)
        self.will_save_settings = False
        if settings:
            try:
                for (k, v) in settings.items():
                    setattr(self, k, v)
                self.will_save_settings = True

            except (AttributeError, TypeError):
                self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
                del context.scene[self.scene_key]

        return ExportHelper.invoke(self, context, event)

    def save_settings(self, context):
        # find all export_ props
        all_props = self.properties
        export_props = {x: getattr(self, x) for x in dir(all_props)
                        if x.startswith("export_") and all_props.get(x) is not None}

        context.scene[self.scene_key] = export_props

    def execute(self, context):
        import os
        import datetime
        from .blender.exp import gltf2_blender_export

        if self.will_save_settings:
            self.save_settings(context)

        if self.export_format == 'GLB':
            self.filename_ext = '.glb'
        else:
            self.filename_ext = '.gltf'

        # All custom export settings are stored in this container.
        export_settings = {}

        export_settings['timestamp'] = datetime.datetime.now()

        export_settings['gltf_filepath'] = bpy.path.ensure_ext(self.filepath, self.filename_ext)
        export_settings['gltf_filedirectory'] = os.path.dirname(export_settings['gltf_filepath']) + '/'

        export_settings['gltf_format'] = self.export_format
        export_settings['gltf_image_format'] = self.export_image_format
        export_settings['gltf_copyright'] = self.export_copyright
        export_settings['gltf_texcoords'] = self.export_texcoords
        export_settings['gltf_normals'] = self.export_normals
        export_settings['gltf_tangents'] = self.export_tangents and self.export_normals

        if self.is_draco_available:
            export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
            export_settings['gltf_draco_mesh_compression_level'] = self.export_draco_mesh_compression_level
            export_settings['gltf_draco_position_quantization'] = self.export_draco_position_quantization
            export_settings['gltf_draco_normal_quantization'] = self.export_draco_normal_quantization
            export_settings['gltf_draco_texcoord_quantization'] = self.export_draco_texcoord_quantization
        else:
            export_settings['gltf_draco_mesh_compression'] = False

        export_settings['gltf_materials'] = self.export_materials
        export_settings['gltf_colors'] = self.export_colors
        export_settings['gltf_cameras'] = self.export_cameras
        export_settings['gltf_selected'] = self.export_selected
        export_settings['gltf_layers'] = True  # self.export_layers
        export_settings['gltf_extras'] = self.export_extras
        export_settings['gltf_yup'] = self.export_yup
        export_settings['gltf_apply'] = self.export_apply
        export_settings['gltf_current_frame'] = self.export_current_frame
        export_settings['gltf_animations'] = self.export_animations
        if self.export_animations:
            export_settings['gltf_frame_range'] = self.export_frame_range
            export_settings['gltf_force_sampling'] = self.export_force_sampling
        else:
            export_settings['gltf_frame_range'] = False
            export_settings['gltf_move_keyframes'] = False
            export_settings['gltf_force_sampling'] = False
        export_settings['gltf_skins'] = self.export_skins
        if self.export_skins:
            export_settings['gltf_all_vertex_influences'] = self.export_all_influences
        else:
            export_settings['gltf_all_vertex_influences'] = False
        export_settings['gltf_frame_step'] = self.export_frame_step
        export_settings['gltf_morph'] = self.export_morph
        if self.export_morph:
            export_settings['gltf_morph_normal'] = self.export_morph_normal
        else:
            export_settings['gltf_morph_normal'] = False
        if self.export_morph and self.export_morph_normal:
            export_settings['gltf_morph_tangent'] = self.export_morph_tangent
        else:
            export_settings['gltf_morph_tangent'] = False

        export_settings['gltf_lights'] = self.export_lights
        export_settings['gltf_displacement'] = self.export_displacement

        export_settings['gltf_binary'] = bytearray()
        export_settings['gltf_binaryfilename'] = os.path.splitext(os.path.basename(
            bpy.path.ensure_ext(self.filepath,self.filename_ext)))[0] + '.bin'

        return gltf2_blender_export.save(context, export_settings)

    def draw(self, context):
        self.layout.prop(self, 'ui_tab', expand=True)
        if self.ui_tab == 'GENERAL':
            self.draw_general_settings()
        elif self.ui_tab == 'MESHES':
            self.draw_mesh_settings()
        elif self.ui_tab == 'OBJECTS':
            self.draw_object_settings()
        elif self.ui_tab == 'MATERIALS':
            self.draw_material_settings()
        elif self.ui_tab == 'ANIMATION':
            self.draw_animation_settings()

    def draw_general_settings(self):
        col = self.layout.box().column()
        col.prop(self, 'export_format')
        col.prop(self, 'export_selected')
        col.prop(self, 'export_apply')
        col.prop(self, 'export_yup')
        col.prop(self, 'export_extras')
        col.prop(self, 'will_save_settings')
        col.prop(self, 'export_copyright')

    def draw_mesh_settings(self):
        col = self.layout.box().column()
        col.prop(self, 'export_texcoords')
        col.prop(self, 'export_normals')
        if self.export_normals:
            col.prop(self, 'export_tangents')
        col.prop(self, 'export_colors')
        col.prop(self, 'export_materials')
        if self.export_materials:
            col.prop(self, 'export_image_format')

        # Add Draco compression option only if the DLL could be found.
        if self.is_draco_available:
            col.prop(self, 'export_draco_mesh_compression_enable')

            # Display options when Draco compression is enabled.
            if self.export_draco_mesh_compression_enable:
                col.prop(self, 'export_draco_mesh_compression_level')
                col.prop(self, 'export_draco_position_quantization')
                col.prop(self, 'export_draco_normal_quantization')
                col.prop(self, 'export_draco_texcoord_quantization')

    def draw_object_settings(self):
        col = self.layout.box().column()
        col.prop(self, 'export_cameras')
        col.prop(self, 'export_lights')

    def draw_animation_settings(self):
        col = self.layout.box().column()
        col.prop(self, 'export_current_frame')
        col.prop(self, 'export_animations')
        if self.export_animations:
            col.prop(self, 'export_frame_range')
            col.prop(self, 'export_frame_step')
            col.prop(self, 'export_force_sampling')
        col.prop(self, 'export_skins')
        if self.export_skins:
            col.prop(self, 'export_all_influences')
        col.prop(self, 'export_morph')
        if self.export_morph:
            col.prop(self, 'export_morph_normal')
            if self.export_morph_normal:
                col.prop(self, 'export_morph_tangent')
Exemple #22
0
class LuxCoreNodeTexImagemap(bpy.types.Node, LuxCoreNodeTexture):
    bl_label = "Imagemap"
    bl_width_default = 200

    def update_image(self, context):
        self.image_user.update(self.image)
        if self.image:
            # Seems like we still need this.
            # User counting does not work reliably with Python PointerProperty.
            # Sometimes, this node is not counted as user.
            self.image.use_fake_user = True
        utils_node.force_viewport_update(self, context)

    image: PointerProperty(name="Image",
                           type=bpy.types.Image,
                           update=update_image)
    image_user: PointerProperty(update=utils_node.force_viewport_update,
                                type=LuxCoreImageUser)

    channel_items = [
        ("default", "Default", "Do not convert the image cannels", 0),
        ("rgb", "RGB", "Use RGB color channels", 1),
        ("red", "Red", "Use only the red color channel", 2),
        ("green", "Green", "Use only the green color channel", 3),
        ("blue", "Blue", "Use only the blue color channel", 4),
        ("alpha", "Alpha", "Use only the alpha channel", 5),
        ("mean", "Mean (Average)", "Greyscale", 6),
        ("colored_mean", "Mean (Luminance)", "Greyscale", 7),
    ]
    channel: EnumProperty(update=utils_node.force_viewport_update,
                          name="Channel",
                          items=channel_items,
                          default="default")

    wrap_items = [
        ("repeat", "Repeat", "", 0),
        ("clamp", "Clamp", "Extend the pixels of the border", 3),
        ("black", "Black", "", 1),
        ("white", "White", "", 2),
    ]
    wrap: EnumProperty(update=utils_node.force_viewport_update,
                       name="Wrap",
                       items=wrap_items,
                       default="repeat")

    gamma: FloatProperty(
        update=utils_node.force_viewport_update,
        name="Gamma",
        default=2.2,
        soft_min=0,
        soft_max=5,
        description="Most LDR images with sRgb colors use gamma 2.2, "
        "while most HDR images with linear colors use gamma 1")
    brightness: FloatProperty(update=utils_node.force_viewport_update,
                              name="Brightness",
                              default=1,
                              description="Brightness multiplier")

    def update_is_normal_map(self, context):
        color_output = self.outputs["Color"]
        bump_output = self.outputs["Bump"]
        alpha_output = self.outputs["Alpha"]
        was_color_enabled = color_output.enabled

        color_output.enabled = not self.is_normal_map
        alpha_output.enabled = not self.is_normal_map
        bump_output.enabled = self.is_normal_map

        utils_node.copy_links_after_socket_swap(color_output, bump_output,
                                                was_color_enabled)
        utils_node.force_viewport_update(self, context)

    is_normal_map: BoolProperty(name="Normalmap",
                                default=False,
                                update=update_is_normal_map,
                                description=NORMAL_MAP_DESC)
    normal_map_scale: FloatProperty(update=utils_node.force_viewport_update,
                                    name="Height",
                                    default=1,
                                    min=0,
                                    soft_max=5,
                                    description=NORMAL_SCALE_DESC)
    normal_map_orientation_items = [
        ("opengl", "OpenGL", "Select if the image is a left-handed normal map",
         0),
        ("directx", "DirectX",
         "Select if the image is a right-handed normal map (inverted green channel)",
         1),
    ]
    normal_map_orientation: EnumProperty(
        update=utils_node.force_viewport_update,
        name="Orientation",
        items=normal_map_orientation_items,
        default="opengl")

    # This function assigns self.image to all faces of all objects using this material
    # and assigns self.image to all image editors that do not have their image pinned.
    def update_set_as_active_uvmap(self, context):
        # TODO 2.8 (Do we even still need this, or can we do something better with Eevee nodes?)
        raise NotImplementedError()
        # if not self.set_as_active_uvmap:
        #     return
        # # Reset button to "unclicked"
        # self["set_as_active_uvmap"] = False
        #
        # if not context.object:
        #     return
        # material = context.object.active_material
        #
        # for obj in context.scene.objects:
        #     for mat_index, slot in enumerate(obj.material_slots):
        #         if slot.material == material:
        #             mesh = obj.data
        #             if hasattr(mesh, "uv_textures") and mesh.uv_textures:
        #                 uv_faces = mesh.uv_textures.active.data
        #                 polygons = mesh.polygons
        #                 # Unfortunately the uv_face has no information about the material
        #                 # that is assigned to the face, so we have to get this information
        #                 # from the polygons of the mesh
        #                 for uv_face, polygon in zip(uv_faces, polygons):
        #                     if polygon.material_index == mat_index:
        #                         uv_face.image = self.image
        #
        # for space in utils_ui.get_all_spaces(context, "IMAGE_EDITOR", "IMAGE_EDITOR"):
        #     # Assign image in all image editors that do not have pinning enabled
        #     if not space.use_image_pin:
        #         space.image = self.image

    # Note: the old "use a property as a button because it is so much simpler" trick
    set_as_active_uvmap: BoolProperty(
        name="Show in Viewport",
        default=False,
        update=update_set_as_active_uvmap,
        description="Show this image map on all objects with this material")

    show_thumbnail: BoolProperty(name="",
                                 default=True,
                                 description="Show thumbnail")

    def init(self, context):
        self.add_input("LuxCoreSocketMapping2D", "2D Mapping")

        self.outputs.new("LuxCoreSocketColor", "Color")
        self.outputs.new("LuxCoreSocketFloatUnbounded", "Alpha")
        self.outputs.new("LuxCoreSocketBump", "Bump")
        self.outputs["Bump"].enabled = False

    def draw_label(self):
        if self.image:
            return self.image.name
        else:
            return self.bl_label

    def draw_buttons(self, context, layout):
        row = layout.row()
        row.prop(self, "show_thumbnail", icon=icons.IMAGE)
        # row.prop(self, "set_as_active_uvmap", toggle=True)  # TODO 2.8
        if self.show_thumbnail:
            layout.template_ID_preview(self, "image", open="image.open")
        else:
            layout.template_ID(self, "image", open="image.open")

        col = layout.column()
        col.active = self.image is not None

        col.prop(self, "is_normal_map")
        if self.image:
            col.prop(self.image, "source")

        if self.is_normal_map:
            col.prop(self, "normal_map_scale")
            col.prop(self, "normal_map_orientation")
        else:
            col.prop(self, "channel")

        col.prop(self, "wrap")

        if not self.is_normal_map:
            col.prop(self, "gamma")
            col.prop(self, "brightness")

        # Info about UV mapping (only show if default is used,
        # when no mapping node is linked)
        if not self.inputs["2D Mapping"].is_linked:
            utils_node.draw_uv_info(context, col)

        self.image_user.draw(col, context.scene)

    def sub_export(self,
                   exporter,
                   depsgraph,
                   props,
                   luxcore_name=None,
                   output_socket=None):
        if self.image is None:
            if self.is_normal_map:
                return [0.5, 0.5, 1.0]
            else:
                return [0, 0, 0]

        if self.image.source == "SEQUENCE":
            frame_change_pre.using_image_sequences = True

        try:
            filepath = ImageExporter.export(self.image, self.image_user,
                                            exporter.scene)
        except OSError as error:
            msg = 'Node "%s" in tree "%s": %s' % (self.name, self.id_data.name,
                                                  error)
            LuxCoreErrorLog.add_warning(msg)
            return [1, 0, 1]

        uvscale, uvrotation, uvdelta = self.inputs["2D Mapping"].export(
            exporter, depsgraph, props)

        definitions = {
            "type": "imagemap",
            "file": filepath,
            "wrap": self.wrap,
            # Mapping
            "mapping.type": "uvmapping2d",
            "mapping.uvscale": uvscale,
            "mapping.rotation": uvrotation,
            "mapping.uvdelta": uvdelta,
        }

        if self.is_normal_map:
            definitions.update({
                "channel":
                "rgb" if self.normal_map_orientation == "opengl" else
                "directx2opengl_normalmap",
                "gamma":
                1,
                "gain":
                1,
            })
        else:
            definitions.update({
                "channel":
                "alpha"
                if output_socket == self.outputs["Alpha"] else self.channel,
                "gamma":
                self.gamma,
                "gain":
                self.brightness,
            })

        luxcore_name = self.create_props(props, definitions, luxcore_name)

        if self.is_normal_map:
            # Implicitly create a normalmap
            tex_name = luxcore_name + "_normalmap"
            helper_prefix = "scene.textures." + tex_name + "."
            helper_defs = {
                "type": "normalmap",
                "texture": luxcore_name,
                "scale": self.normal_map_scale,
            }
            props.Set(utils.create_props(helper_prefix, helper_defs))

            # The helper texture gets linked in front of this node
            return tex_name
        else:
            return luxcore_name
Exemple #23
0
class ShapeTransfer(Operator):
    """Copy another selected objects active shape to this one by """ \
    """applying the relative offsets"""

    bl_idname = "object.shape_key_transfer"
    bl_label = "Transfer Shape Key"
    bl_options = {'REGISTER', 'UNDO'}

    mode = EnumProperty(
        items=(
            (
                'OFFSET',
                "Offset",
                "Apply the relative positional offset",
            ),
            (
                'RELATIVE_FACE',
                "Relative Face",
                "Calculate relative position (using faces)",
            ),
            (
                'RELATIVE_EDGE',
                "Relative Edge",
                "Calculate relative position (using edges)",
            ),
        ),
        name="Transformation Mode",
        description="Relative shape positions to the new shape method",
        default='OFFSET',
    )
    use_clamp = BoolProperty(
        name="Clamp Offset",
        description=("Clamp the transformation to the distance each "
                     "vertex moves in the original shape"),
        default=False,
    )

    def _main(self, ob_act, objects, mode='OFFSET', use_clamp=False):
        def me_nos(verts):
            return [v.normal.copy() for v in verts]

        def me_cos(verts):
            return [v.co.copy() for v in verts]

        def ob_add_shape(ob, name):
            me = ob.data
            key = ob.shape_key_add(from_mix=False)
            if len(me.shape_keys.key_blocks) == 1:
                key.name = "Basis"
                key = ob.shape_key_add(from_mix=False)  # we need a rest
            key.name = name
            ob.active_shape_key_index = len(me.shape_keys.key_blocks) - 1
            ob.show_only_shape_key = True

        from mathutils.geometry import barycentric_transform
        from mathutils import Vector

        if use_clamp and mode == 'OFFSET':
            use_clamp = False

        me = ob_act.data
        orig_key_name = ob_act.active_shape_key.name

        orig_shape_coords = me_cos(ob_act.active_shape_key.data)

        orig_normals = me_nos(me.vertices)
        # actual mesh vertex location isn't as reliable as the base shape :S
        #~ orig_coords = me_cos(me.vertices)
        orig_coords = me_cos(me.shape_keys.key_blocks[0].data)

        for ob_other in objects:
            me_other = ob_other.data
            if len(me_other.vertices) != len(me.vertices):
                self.report({'WARNING'},
                            ("Skipping '%s', "
                             "vertex count differs") % ob_other.name)
                continue

            target_normals = me_nos(me_other.vertices)
            if me_other.shape_keys:
                target_coords = me_cos(me_other.shape_keys.key_blocks[0].data)
            else:
                target_coords = me_cos(me_other.vertices)

            ob_add_shape(ob_other, orig_key_name)

            # editing the final coords, only list that stores wrapped coords
            target_shape_coords = [
                v.co for v in ob_other.active_shape_key.data
            ]

            median_coords = [[] for i in range(len(me.vertices))]

            # Method 1, edge
            if mode == 'OFFSET':
                for i, vert_cos in enumerate(median_coords):
                    vert_cos.append(target_coords[i] +
                                    (orig_shape_coords[i] - orig_coords[i]))

            elif mode == 'RELATIVE_FACE':
                for poly in me.polygons:
                    idxs = poly.vertices[:]
                    v_before = idxs[-2]
                    v = idxs[-1]
                    for v_after in idxs:
                        pt = barycentric_transform(
                            orig_shape_coords[v],
                            orig_coords[v_before],
                            orig_coords[v],
                            orig_coords[v_after],
                            target_coords[v_before],
                            target_coords[v],
                            target_coords[v_after],
                        )
                        median_coords[v].append(pt)
                        v_before = v
                        v = v_after

            elif mode == 'RELATIVE_EDGE':
                for ed in me.edges:
                    i1, i2 = ed.vertices
                    v1, v2 = orig_coords[i1], orig_coords[i2]
                    edge_length = (v1 - v2).length
                    n1loc = v1 + orig_normals[i1] * edge_length
                    n2loc = v2 + orig_normals[i2] * edge_length

                    # now get the target nloc's
                    v1_to, v2_to = target_coords[i1], target_coords[i2]
                    edlen_to = (v1_to - v2_to).length
                    n1loc_to = v1_to + target_normals[i1] * edlen_to
                    n2loc_to = v2_to + target_normals[i2] * edlen_to

                    pt = barycentric_transform(orig_shape_coords[i1], v2, v1,
                                               n1loc, v2_to, v1_to, n1loc_to)
                    median_coords[i1].append(pt)

                    pt = barycentric_transform(orig_shape_coords[i2], v1, v2,
                                               n2loc, v1_to, v2_to, n2loc_to)
                    median_coords[i2].append(pt)

            # apply the offsets to the new shape
            from functools import reduce
            VectorAdd = Vector.__add__

            for i, vert_cos in enumerate(median_coords):
                if vert_cos:
                    co = reduce(VectorAdd, vert_cos) / len(vert_cos)

                    if use_clamp:
                        # clamp to the same movement as the original
                        # breaks copy between different scaled meshes.
                        len_from = (orig_shape_coords[i] -
                                    orig_coords[i]).length
                        ofs = co - target_coords[i]
                        ofs.length = len_from
                        co = target_coords[i] + ofs

                    target_shape_coords[i][:] = co

        return {'FINISHED'}

    @classmethod
    def poll(cls, context):
        obj = context.active_object
        return (obj and obj.mode != 'EDIT')

    def execute(self, context):
        ob_act = context.active_object
        objects = [
            ob for ob in context.selected_editable_objects if ob != ob_act
        ]

        if 1:  # swap from/to, means we cant copy to many at once.
            if len(objects) != 1:
                self.report({'ERROR'}, ("Expected one other selected "
                                        "mesh object to copy from"))

                return {'CANCELLED'}
            ob_act, objects = objects[0], [ob_act]

        if ob_act.type != 'MESH':
            self.report({'ERROR'}, "Other object is not a mesh")
            return {'CANCELLED'}

        if ob_act.active_shape_key is None:
            self.report({'ERROR'}, "Other object has no shape key")
            return {'CANCELLED'}
        return self._main(ob_act, objects, self.mode, self.use_clamp)
Exemple #24
0
class FractureCell(Operator):
    bl_idname = "object.add_fracture_cell_objects"
    bl_label = "Cell fracture selected mesh objects"
    bl_options = {'PRESET'}

    # -------------------------------------------------------------------------
    # Source Options
    source: EnumProperty(
            name="Source",
            items=(('VERT_OWN', "Own Verts", "Use own vertices"),
                   ('VERT_CHILD', "Child Verts", "Use child object vertices"),
                   ('PARTICLE_OWN', "Own Particles", ("All particle systems of the "
                                                      "source object")),
                   ('PARTICLE_CHILD', "Child Particles", ("All particle systems of the "
                                                          "child objects")),
                   ('PENCIL', "Annotation Pencil", "Annotation Grease Pencil."),
                   ),
            options={'ENUM_FLAG'},
            default={'PARTICLE_OWN'},
            )

    source_limit: IntProperty(
            name="Source Limit",
            description="Limit the number of input points, 0 for unlimited",
            min=0, max=5000,
            default=100,
            )

    source_noise: FloatProperty(
            name="Noise",
            description="Randomize point distribution",
            min=0.0, max=1.0,
            default=0.0,
            )

    cell_scale: FloatVectorProperty(
            name="Scale",
            description="Scale Cell Shape",
            size=3,
            min=0.0, max=1.0,
            default=(1.0, 1.0, 1.0),
            )

    # -------------------------------------------------------------------------
    # Recursion

    recursion: IntProperty(
            name="Recursion",
            description="Break shards recursively",
            min=0, max=5000,
            default=0,
            )

    recursion_source_limit: IntProperty(
            name="Source Limit",
            description="Limit the number of input points, 0 for unlimited (applies to recursion only)",
            min=0, max=5000,
            default=8,
            )

    recursion_clamp: IntProperty(
            name="Clamp Recursion",
            description="Finish recursion when this number of objects is reached (prevents recursing for extended periods of time), zero disables",
            min=0, max=10000,
            default=250,
            )

    recursion_chance: FloatProperty(
            name="Random Factor",
            description="Likelihood of recursion",
            min=0.0, max=1.0,
            default=0.25,
            )

    recursion_chance_select: EnumProperty(
            name="Recurse Over",
            items=(('RANDOM', "Random", ""),
                   ('SIZE_MIN', "Small", "Recursively subdivide smaller objects"),
                   ('SIZE_MAX', "Big", "Recursively subdivide bigger objects"),
                   ('CURSOR_MIN', "Cursor Close", "Recursively subdivide objects closer to the cursor"),
                   ('CURSOR_MAX', "Cursor Far", "Recursively subdivide objects farther from the cursor"),
                   ),
            default='SIZE_MIN',
            )

    # -------------------------------------------------------------------------
    # Mesh Data Options

    use_smooth_faces: BoolProperty(
            name="Smooth Interior",
            description="Smooth Faces of inner side",
            default=False,
            )

    use_sharp_edges: BoolProperty(
            name="Sharp Edges",
            description="Set sharp edges when disabled",
            default=True,
            )

    use_sharp_edges_apply: BoolProperty(
            name="Apply Split Edge",
            description="Split sharp hard edges",
            default=True,
            )

    use_data_match: BoolProperty(
            name="Match Data",
            description="Match original mesh materials and data layers",
            default=True,
            )

    use_island_split: BoolProperty(
            name="Split Islands",
            description="Split disconnected meshes",
            default=True,
            )

    margin: FloatProperty(
            name="Margin",
            description="Gaps for the fracture (gives more stable physics)",
            min=0.0, max=1.0,
            default=0.001,
            )

    material_index: IntProperty(
            name="Material",
            description="Material index for interior faces",
            default=0,
            )

    use_interior_vgroup: BoolProperty(
            name="Interior VGroup",
            description="Create a vertex group for interior verts",
            default=False,
            )

    # -------------------------------------------------------------------------
    # Physics Options

    mass_mode: EnumProperty(
            name="Mass Mode",
            items=(('VOLUME', "Volume", "Objects get part of specified mass based on their volume"),
                   ('UNIFORM', "Uniform", "All objects get the specified mass"),
                   ),
            default='VOLUME',
            )

    mass: FloatProperty(
            name="Mass",
            description="Mass to give created objects",
            min=0.001, max=1000.0,
            default=1.0,
            )


    # -------------------------------------------------------------------------
    # Object Options

    use_recenter: BoolProperty(
            name="Recenter",
            description="Recalculate the center points after splitting",
            default=True,
            )

    use_remove_original: BoolProperty(
            name="Remove Original",
            description="Removes the parents used to create the shatter",
            default=True,
            )

    # -------------------------------------------------------------------------
    # Scene Options
    #
    # .. different from object options in that this controls how the objects
    #    are setup in the scene.

    collection_name: StringProperty(
            name="Collection",
            description="Create objects in a collection "
                        "(use existing or create new)",
            )

    # -------------------------------------------------------------------------
    # Debug
    use_debug_points: BoolProperty(
            name="Debug Points",
            description="Create mesh data showing the points used for fracture",
            default=False,
            )

    use_debug_redraw: BoolProperty(
            name="Show Progress Realtime",
            description="Redraw as fracture is done",
            default=True,
            )

    use_debug_bool: BoolProperty(
            name="Debug Boolean",
            description="Skip applying the boolean modifier",
            default=False,
            )

    def execute(self, context):
        keywords = self.as_keywords()  # ignore=("blah",)

        main(context, **keywords)

        return {'FINISHED'}


    def invoke(self, context, event):
        # print(self.recursion_chance_select)
        wm = context.window_manager
        return wm.invoke_props_dialog(self, width=600)

    def draw(self, context):
        layout = self.layout
        box = layout.box()
        col = box.column()
        col.label(text="Point Source")
        rowsub = col.row()
        rowsub.prop(self, "source")
        rowsub = col.row()
        rowsub.prop(self, "source_limit")
        rowsub.prop(self, "source_noise")
        rowsub = col.row()
        rowsub.prop(self, "cell_scale")

        box = layout.box()
        col = box.column()
        col.label(text="Recursive Shatter")
        rowsub = col.row(align=True)
        rowsub.prop(self, "recursion")
        rowsub.prop(self, "recursion_source_limit")
        rowsub.prop(self, "recursion_clamp")
        rowsub = col.row()
        rowsub.prop(self, "recursion_chance")
        rowsub.prop(self, "recursion_chance_select", expand=True)

        box = layout.box()
        col = box.column()
        col.label(text="Mesh Data")
        rowsub = col.row()
        rowsub.prop(self, "use_smooth_faces")
        rowsub.prop(self, "use_sharp_edges")
        rowsub.prop(self, "use_sharp_edges_apply")
        rowsub.prop(self, "use_data_match")
        rowsub = col.row()

        # on same row for even layout but infact are not all that related
        rowsub.prop(self, "material_index")
        rowsub.prop(self, "use_interior_vgroup")

        # could be own section, control how we subdiv
        rowsub.prop(self, "margin")
        rowsub.prop(self, "use_island_split")


        box = layout.box()
        col = box.column()
        col.label(text="Physics")
        rowsub = col.row(align=True)
        rowsub.prop(self, "mass_mode")
        rowsub.prop(self, "mass")


        box = layout.box()
        col = box.column()
        col.label(text="Object")
        rowsub = col.row(align=True)
        rowsub.prop(self, "use_recenter")


        box = layout.box()
        col = box.column()
        col.label(text="Scene")
        rowsub = col.row(align=True)
        rowsub.prop(self, "collection_name")

        box = layout.box()
        col = box.column()
        col.label(text="Debug")
        rowsub = col.row(align=True)
        rowsub.prop(self, "use_debug_redraw")
        rowsub.prop(self, "use_debug_points")
        rowsub.prop(self, "use_debug_bool")
Exemple #25
0
class BGIS_OT_add_predef_crs(Operator):
    bl_idname = "bgis.add_predef_crs"
    bl_description = 'Add predefinate CRS'
    bl_label = "Add"
    bl_options = {'INTERNAL'}

    crs: StringProperty(
        name="Definition",
        description="Specify EPSG code or Proj4 string definition for this CRS"
    )
    desc: StringProperty(name="Description",
                         description="Choose a convenient name for this CRS")

    def check(self, context):
        return True

    def search(self, context):
        if not EPSGIO.ping():
            self.report({'ERROR'}, "Cannot request epsg.io website")
        else:
            results = EPSGIO.search(self.query)
            self.results = json.dumps(results)
            if results:
                self.crs = 'EPSG:' + results[0]['code']
                self.desc = results[0]['name']

    def updEnum(self, context):
        crsItems = []
        if self.results != '':
            for result in json.loads(self.results):
                srid = 'EPSG:' + result['code']
                crsItems.append((result['code'], result['name'], srid))
        return crsItems

    def fill(self, context):
        if self.results != '':
            crs = [
                crs for crs in json.loads(self.results)
                if crs['code'] == self.crsEnum
            ][0]
            self.crs = 'EPSG:' + crs['code']
            self.desc = crs['name']

    query: StringProperty(name='Query',
                          description='Hit enter to process the search',
                          update=search)

    results: StringProperty()

    crsEnum: EnumProperty(name='Results',
                          description='Select the desired CRS',
                          items=updEnum,
                          update=fill)

    search: BoolProperty(
        name='Search',
        description='Search for coordinate system into EPSG database',
        default=False)

    save: BoolProperty(
        name='Save to addon preferences',
        description='Save Blender user settings after the addition',
        default=False)

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)  #, width=300)

    def draw(self, context):
        layout = self.layout
        layout.prop(self, 'search')
        if self.search:
            layout.prop(self, 'query')
            layout.prop(self, 'crsEnum')
            layout.separator()
        layout.prop(self, 'crs')
        layout.prop(self, 'desc')
        layout.prop(self, 'save')

    def execute(self, context):
        prefs = context.preferences.addons[PKG].preferences
        #append the new crs def to json string
        data = json.loads(prefs.predefCrsJson)
        if not SRS.validate(self.crs):
            self.report({'ERROR'}, 'Invalid CRS')
        if self.crs.isdigit():
            self.crs = 'EPSG:' + self.crs
        data[self.crs] = self.desc
        prefs.predefCrsJson = json.dumps(data)
        #change enum index to new added crs and redraw
        #prefs.predefCrs = self.crs
        context.area.tag_redraw()
        #end
        if self.save:
            bpy.ops.wm.save_userpref()
        return {'FINISHED'}
Exemple #26
0
class SmartVert(bpy.types.Operator):
    bl_idname = "machin3.smart_vert"
    bl_label = "MACHIN3: Smart Vert"
    bl_options = {'REGISTER', 'UNDO'}

    mode: EnumProperty(name="Mode", items=modeitems, default="MERGE")
    mergetype: EnumProperty(name="Merge Type", items=mergetypeitems, default="LAST")
    pathtype: EnumProperty(name="Path Type", items=pathtypeitems, default="TOPO")

    slideoverride: BoolProperty(name="Slide Override", default=False)

    # hidden
    wrongselection = False

    @classmethod
    def poll(cls, context):
        if context.mode == 'EDIT_MESH' and tuple(context.scene.tool_settings.mesh_select_mode) == (True, False, False):
            bm = bmesh.from_edit_mesh(context.active_object.data)
            return [v for v in bm.verts if v.select]

    def draw(self, context):
        layout = self.layout

        column = layout.column()

        if not self.slideoverride:
            row = column.split(factor=0.3)
            row.label(text="Mode")
            r = row.row()
            r.prop(self, "mode", expand=True)

            if self.mode == "MERGE":
                row = column.split(factor=0.3)
                row.label(text="Merge")
                r = row.row()
                r.prop(self, "mergetype", expand=True)

            if self.mode == "CONNECT" or (self.mode == "MERGE" and self.mergetype == "PATHS"):
                if self.wrongselection:
                    column.label(text="You need to select exactly 4 vertices for paths.", icon="INFO")

                else:
                    row = column.split(factor=0.3)
                    row.label(text="Shortest Path")
                    r = row.row()
                    r.prop(self, "pathtype", expand=True)

    def draw_VIEW3D(self, args):
        shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
        shader.bind()

        bgl.glEnable(bgl.GL_BLEND)
        bgl.glDepthFunc(bgl.GL_ALWAYS)

        bgl.glLineWidth(3)
        shader.uniform_float("color", (0.5, 1, 0.5, 0.5))
        batch = batch_for_shader(shader, 'LINES', {"pos": self.coords}, indices=self.edge_indices)
        batch.draw(shader)

        bgl.glDisable(bgl.GL_BLEND)

    def modal(self, context, event):
        context.area.tag_redraw()

        # update mouse postion for HUD
        if event.type == "MOUSEMOVE":
            self.mouse_x = event.mouse_region_x
            self.mouse_y = event.mouse_region_y

        events = ["MOUSEMOVE"]

        if event.type in events:
            wrap_mouse(self, context, event, x=True)

            offset_x = self.mouse_x - self.last_mouse_x

            divisor = 5000 if event.shift else 50 if event.ctrl else 500

            delta_x = offset_x / divisor
            self.distance += delta_x

            # modal slide
            self.slide(context, self.distance)


        # VIEWPORT control

        elif event.type in {'MIDDLEMOUSE'}:
            return {'PASS_THROUGH'}

        # FINISH

        elif event.type in {'LEFTMOUSE', 'SPACE'}:
            bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW')
            return {'FINISHED'}

        # CANCEL

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            self.cancel_modal()
            return {'CANCELLED'}

        self.last_mouse_x = self.mouse_x

        return {'RUNNING_MODAL'}

    def cancel_modal(self):
        bpy.types.SpaceView3D.draw_handler_remove(self.VIEW3D, 'WINDOW')

        bpy.ops.object.mode_set(mode='OBJECT')
        self.initbm.to_mesh(self.active.data)
        bpy.ops.object.mode_set(mode='EDIT')

    def invoke(self, context, event):
        # SLIDE EXTEND
        if self.slideoverride:
            bm = bmesh.from_edit_mesh(context.active_object.data)
            verts = [v for v in bm.verts if v.select]

            if len(verts) > 1:
                self.active = context.active_object

                # make sure the current edit mode state is saved to obj.data
                self.active.update_from_editmode()

                # save this initial mesh state, this will be used when canceling the modal and to reset it for each mousemove event
                self.initbm = bmesh.new()
                self.initbm.from_mesh(self.active.data)

                # mouse positions
                self.mouse_x = self.last_mouse_x = event.mouse_region_x
                self.distance = 1

                # initial run:
                self.slide(context, self.distance)

                args = (self, context)
                self.VIEW3D = bpy.types.SpaceView3D.draw_handler_add(self.draw_VIEW3D, (args, ), 'WINDOW', 'POST_VIEW')

                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}

        # MERGE and CONNECT
        else:
            self.smart_vert(context)

        return {'FINISHED'}

    def execute(self, context):
        self.smart_vert(context)
        return {'FINISHED'}

    def smart_vert(self, context):
        active = context.active_object
        topo = True if self.pathtype == "TOPO" else False

        bm = bmesh.from_edit_mesh(active.data)
        bm.normal_update()
        bm.verts.ensure_lookup_table()

        verts = [v for v in bm.verts if v.select]

        # MERGE

        if self.mode == "MERGE":

            if self.mergetype == "LAST":
                if len(verts) >= 2:
                    if self.validate_history(active, bm, lazy=True):
                        bpy.ops.mesh.merge(type='LAST')

            elif self.mergetype == "CENTER":
                if len(verts) >= 2:
                    bpy.ops.mesh.merge(type='CENTER')

            elif self.mergetype == "PATHS":
                self.wrongselection = False

                if len(verts) == 4:
                    history = self.validate_history(active, bm)

                    if history:
                        path1, path2 = self.get_paths(bm, history, topo)

                        self.weld(active, bm, path1, path2)
                        return

                self.wrongselection = True

        # CONNECT

        elif self.mode == "CONNECT":
            self.wrongselection = False

            if len(verts) == 4:
                history = self.validate_history(active, bm)

                if history:
                    path1, path2 = self.get_paths(bm, history, topo)

                    self.connect(active, bm, path1, path2)
                    return

            self.wrongselection = True

    def get_paths(self, bm, history, topo):
        pair1 = history[0:2]
        pair2 = history[2:4]
        pair2.reverse()

        path1 = get_shortest_path(bm, *pair1, topo=topo, select=True)
        path2 = get_shortest_path(bm, *pair2, topo=topo, select=True)

        return path1, path2

    def validate_history(self, active, bm, lazy=False):
        verts = [v for v in bm.verts if v.select]
        history = list(bm.select_history)

        # just check for the prence of any element in the history
        if lazy:
            return history

        if len(verts) == len(history):
            return history
        return None

    def weld(self, active, bm, path1, path2):
        targetmap = {}
        for v1, v2 in zip(path1, path2):
            targetmap[v1] = v2

        bmesh.ops.weld_verts(bm, targetmap=targetmap)

        bmesh.update_edit_mesh(active.data)

    def connect(self, active, bm, path1, path2):
        for verts in zip(path1, path2):
            if not bm.edges.get(verts):
                bmesh.ops.connect_vert_pair(bm, verts=verts)

        bmesh.update_edit_mesh(active.data)

    def slide(self, context, distance):
        mx = self.active.matrix_world

        bpy.ops.object.mode_set(mode='OBJECT')

        bm = self.initbm.copy()

        history = list(bm.select_history)

        last = history[-1]
        verts = [v for v in bm.verts if v.select and v != last]

        self.coords = []
        self.edge_indices = []

        self.coords.append(mx @ last.co)

        for idx, v in enumerate(verts):
            v.co = last.co + (v.co - last.co) * distance

            self.coords.append(mx @ v.co)
            self.edge_indices.append((0, idx + 1))


        bm.to_mesh(self.active.data)

        bpy.ops.object.mode_set(mode='EDIT')
class ClippingToggle(bpy.types.Operator):
    bl_idname = "machin3.clipping_toggle"
    bl_label = "MACHIN3: 视图剪切切换"
    bl_options = {'REGISTER', 'UNDO'}

    def update_clip_start_maximum(self, context):
        if self.avoid_item_update:
            self.avoid_item_update = False
            return

        bpy.context.space_data.clip_start = self.maximum
        self.avoid_state_update = True
        self.state = "MAX"
        self.avoid_execute = True

    def update_clip_start_medium(self, context):
        if self.avoid_item_update:
            self.avoid_item_update = False
            return

        bpy.context.space_data.clip_start = self.medium
        self.avoid_state_update = True
        self.state = "MED"
        self.avoid_execute = True

    def update_clip_start_minimum(self, context):
        if self.avoid_item_update:
            self.avoid_item_update = False
            return

        bpy.context.space_data.clip_start = self.minimum
        self.avoid_state_update = True
        self.state = "MIN"
        self.avoid_execute = True

    def update_state(self, context):
        if self.avoid_execute:
            self.avoid_execute = False
            return

        if self.avoid_state_update:
            self.avoid_state_update = False
            return

        view = bpy.context.space_data

        if self.state == "MIN":
            view.clip_start = self.minimum

        elif self.state == "MED":
            view.clip_start = self.medium

        elif self.state == "MAX":
            view.clip_start = self.maximum

        self.avoid_execute = True

    def update_reset(self, context):
        if not self.reset:
            return

        self.avoid_item_update = True
        self.maximum = 1
        self.avoid_item_update = True
        self.medium = 0.1
        self.avoid_item_update = True
        self.minimum = 0.001

        view = bpy.context.space_data

        if self.state == "MIN":
            view.clip_start = self.minimum

        elif self.state == "MED":
            view.clip_start = self.medium

        elif self.state == "MAX":
            view.clip_start = self.maximum

        self.reset = False
        self.avoid_execute = True

    maximum: FloatProperty(name="最大值",
                           default=1,
                           min=0,
                           precision=2,
                           step=10,
                           update=update_clip_start_maximum)
    medium: FloatProperty(name="中间值",
                          default=0.1,
                          min=0,
                          precision=3,
                          step=1,
                          update=update_clip_start_medium)
    minimum: FloatProperty(name="最小值",
                           default=0.001,
                           min=0,
                           precision=5,
                           step=0.001,
                           update=update_clip_start_minimum)

    state: EnumProperty(name="当前状态",
                        items=state_items,
                        default="MED",
                        update=update_state)
    reset: BoolProperty(default=False, update=update_reset)

    avoid_execute: BoolProperty(default=False)
    avoid_state_update: BoolProperty(default=False)
    avoid_item_update: BoolProperty(default=False)

    def draw(self, context):
        layout = self.layout
        col = layout.column()

        view = bpy.context.space_data

        row = col.row(align=True)
        row.prop(self, "state", expand=True)
        row.prop(self, "reset", text="", icon="BLANK1", emboss=False)

        row = col.row(align=True)
        row.prop(self, "minimum", text="")
        row.prop(self, "medium", text="")
        row.prop(self, "maximum", text="")
        row.prop(self, "reset", text="", icon="LOOP_BACK")

        row = col.row(align=True)
        row.label(text="当前")
        row.label(text=str(round(view.clip_start, 6)))

    def execute(self, context):
        if self.avoid_execute:
            self.avoid_execute = False

        else:
            self.avoid_execute = True
            self.state = step_enum(self.state, state_items, 1, loop=True)

            view = bpy.context.space_data

            if self.state == "MIN":
                view.clip_start = self.minimum

            elif self.state == "MED":
                view.clip_start = self.medium

            elif self.state == "MAX":
                view.clip_start = self.maximum

        return {'FINISHED'}
class SunPosProperties(PropertyGroup):
    usage_mode: EnumProperty(
        name="Usage mode",
        description="Operate in normal mode or environment texture mode",
        items=(
            ('NORMAL', "Normal", ""),
            ('HDR', "Sun + HDR texture", ""),
        ),
        default='NORMAL',
        update=sun_update)

    use_daylight_savings: BoolProperty(
        name="Daylight savings",
        description="Daylight savings time adds 1 hour to standard time",
        default=False,
        update=sun_update)

    use_refraction: BoolProperty(
        name="Use refraction",
        description="Show apparent sun position due to refraction",
        default=True,
        update=sun_update)

    show_north: BoolProperty(name="Show North",
                             description="Draw line pointing north",
                             default=False,
                             update=north_update)

    north_offset: FloatProperty(
        name="North Offset",
        description="Rotate the scene to choose North direction",
        unit="ROTATION",
        soft_min=-pi,
        soft_max=pi,
        step=10.0,
        default=0.0,
        update=sun_update)

    latitude: FloatProperty(name="Latitude",
                            description="Latitude: (+) Northern (-) Southern",
                            soft_min=-90.0,
                            soft_max=90.0,
                            step=5,
                            precision=3,
                            default=0.0,
                            update=sun_update)

    longitude: FloatProperty(
        name="Longitude",
        description="Longitude: (-) West of Greenwich (+) East of Greenwich",
        soft_min=-180.0,
        soft_max=180.0,
        step=5,
        precision=3,
        default=0.0,
        update=sun_update)

    co_parser: StringProperty(
        name="Enter coordinates",
        description="Enter coordinates from an online map",
        update=parse_coordinates)

    month: IntProperty(name="Month",
                       min=1,
                       max=12,
                       default=TODAY.month,
                       update=sun_update)

    day: IntProperty(name="Day",
                     min=1,
                     max=31,
                     default=TODAY.day,
                     update=sun_update)

    year: IntProperty(name="Year",
                      min=1,
                      max=4000,
                      default=TODAY.year,
                      update=sun_update)

    use_day_of_year: BoolProperty(
        description="Use a single value for day of year",
        name="Use day of year",
        default=False,
        update=sun_update)

    day_of_year: IntProperty(name="Day of year",
                             min=1,
                             max=366,
                             default=1,
                             update=sun_update)

    UTC_zone: FloatProperty(
        name="UTC zone",
        description="Time zone: Difference from Greenwich, England in hours",
        precision=1,
        min=-14.0,
        max=13,
        step=50,
        default=0.0,
        update=sun_update)

    time: FloatProperty(name="Time",
                        description="Time of the day",
                        precision=4,
                        soft_min=0.0,
                        soft_max=23.9999,
                        step=1.0,
                        default=12.0,
                        update=sun_update)

    sun_distance: FloatProperty(name="Distance",
                                description="Distance to sun from origin",
                                unit="LENGTH",
                                min=0.0,
                                soft_max=3000.0,
                                step=10.0,
                                default=50.0,
                                update=sun_update)

    sun_object: PointerProperty(name="Sun Object",
                                type=bpy.types.Object,
                                description="Sun object to set in the scene",
                                poll=lambda self, obj: obj.type == 'LIGHT',
                                update=sun_update)

    object_collection: PointerProperty(
        name="Collection",
        type=bpy.types.Collection,
        description="Collection of objects used to visualize sun motion",
        update=sun_update)

    object_collection_type: EnumProperty(
        name="Display type",
        description="Show object group as sun motion",
        items=(
            ('ANALEMMA', "Analemma", ""),
            ('DIURNAL', "Diurnal", ""),
        ),
        default='ANALEMMA',
        update=sun_update)

    sky_texture: StringProperty(name="Sky Texture",
                                default="Sky Texture",
                                description="Name of sky texture to be used",
                                update=sun_update)

    hdr_texture: StringProperty(
        default="Environment Texture",
        name="Environment Texture",
        description="Name of texture to use. World nodes must be enabled "
        "and color set to Environment Texture",
        update=sun_update)

    hdr_azimuth: FloatProperty(
        name="Rotation",
        description="Rotation angle of sun and environment texture",
        unit="ROTATION",
        step=10.0,
        default=0.0,
        precision=3,
        update=sun_update)

    hdr_elevation: FloatProperty(name="Elevation",
                                 description="Elevation angle of sun",
                                 unit="ROTATION",
                                 step=10.0,
                                 default=0.0,
                                 precision=3,
                                 update=sun_update)

    bind_to_sun: BoolProperty(
        description="If true, Environment texture moves with sun",
        default=False,
        update=sun_update)

    time_spread: FloatProperty(
        name="Time Spread",
        description="Time period in which to spread object collection",
        precision=4,
        soft_min=1.0,
        soft_max=24.0,
        step=1.0,
        default=23.0,
        update=sun_update)
Exemple #29
0
class SvDupliInstancesLite(bpy.types.Node, SverchCustomTreeNode, SvViewerNode):
    """
    Triggers: Fast duplication
    Tooltip: Create Instances from parent object + child
    """
    bl_idname = 'SvDupliInstancesLite'
    bl_label = 'Dupli Instancer Lite'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_DUPLI_INSTANCER'

    def update_sockets(self, context):
        if self.mode == 'FACES' and self.scale:
            self.inputs['Scale'].hide_safe = False
        else:
            self.inputs['Scale'].hide_safe = True
        updateNode(self, context)

    scale: BoolProperty(default=False,
                        description="scale children",
                        update=update_sockets)
    scale_factor: FloatProperty(default=1,
                                name='Scale',
                                description="Children scale factor",
                                update=updateNode)
    align: BoolProperty(default=False,
                        description="align with vertex normal",
                        update=updateNode)

    show_instancer_for_viewport: BoolProperty(
        default=False,
        description="Show instancer in viewport",
        update=updateNode)
    show_instancer_for_render: BoolProperty(
        default=False,
        description="Show instancer in render",
        update=updateNode)

    show_base_child: BoolProperty(name='Show base child',
                                  default=True,
                                  description="Hide base object in viewport",
                                  update=updateNode)

    auto_release: BoolProperty(
        name='Auto Release',
        description='Remove childs not called by this node',
        update=updateNode)

    modes = [("VERTS", "Verts", "On vertices. Only Translation is used", "",
              1),
             ("FACES", "Polys",
              "On polygons. Translation, Rotation and Scale supported", "", 2)]
    T = [
        ("POS_X", "X", "", "", 1),
        ("POS_Y", "Y", "", "", 2),
        ("POS_Z", "Z", "", "", 3),
        ("NEG_X", "-X", "", "", 4),
        ("NEG_Y", "-Y", "", "", 5),
        ("NEG_Z", "-Z", "", "", 6),
    ]

    mode: EnumProperty(name='Mode',
                       items=modes,
                       default='VERTS',
                       update=update_sockets)

    U = ['X', 'Y', 'Z']

    track: EnumProperty(name="track",
                        default=T[0][0],
                        items=T,
                        update=updateNode)
    up: EnumProperty(name="up", default=U[2], items=e(U), update=updateNode)

    def sv_init(self, context):
        self.width = 160
        self.inputs.new("SvObjectSocket", "Parent")
        self.inputs.new("SvObjectSocket", "Child")
        self.sv_new_input("SvStringsSocket",
                          "Scale",
                          hide_safe=True,
                          prop_name='scale_factor')
        self.outputs.new("SvObjectSocket", "Parent")
        self.outputs.new("SvObjectSocket", "Child")

    def draw_buttons(self, context, layout):
        layout.row(align=True).prop(self, "mode", expand=True)
        col = layout.column(align=True)
        row = col.row(align=True)
        row.label(text='Instancer')
        row.prop(
            self,
            'show_instancer_for_viewport',
            text='',
            toggle=True,
            icon=
            f"RESTRICT_VIEW_{'OFF' if self.show_instancer_for_viewport else 'ON'}"
        )
        row.prop(
            self,
            'show_instancer_for_render',
            text='',
            toggle=True,
            icon=
            f"RESTRICT_RENDER_{'OFF' if self.show_instancer_for_render else 'ON'}"
        )

        row = col.row(align=True)
        row.label(text='Child')
        row.prop(self,
                 'show_base_child',
                 text='',
                 toggle=True,
                 icon=f"HIDE_{'OFF' if self.show_base_child else 'ON'}")
        if self.mode == 'FACES':
            row.prop(self,
                     'scale',
                     text='',
                     icon_value=custom_icon('SV_SCALE'),
                     toggle=True)
        else:
            row.prop(self,
                     'align',
                     text='',
                     icon='MOD_NORMALEDIT',
                     toggle=True)

        row.prop(self, 'auto_release', text='', toggle=True, icon='UNLINKED')
        if self.mode == 'VERTS' and self.align:
            layout.prop(self, 'track')
            layout.prop(self, 'up')

    def set_parent_props(self, parent, scale):
        parent.instance_type = self.mode
        parent.use_instance_vertices_rotation = self.align
        parent.use_instance_faces_scale = self.scale
        parent.show_instancer_for_viewport = self.show_instancer_for_viewport
        parent.show_instancer_for_render = self.show_instancer_for_render
        parent.instance_faces_scale = scale

    def set_child_props(self, parent, child):
        child.parent = parent
        child.track_axis = self.track
        child.up_axis = self.up
        child.hide_set(not self.show_base_child)

    def process(self):
        print(True)
        parent_objects = self.inputs['Parent'].sv_get(deepcopy=False)
        child_objects = self.inputs['Child'].sv_get(deepcopy=False)
        scale = self.inputs['Scale'].sv_get(deepcopy=False)
        if not child_objects or not parent_objects:
            return

        if len(parent_objects) == 1 or len(child_objects) == 1:
            parent = parent_objects[0]
            self.set_parent_props(parent, scale[0][0])
            childs_name = []
            for child in child_objects:
                self.set_child_props(parent, child)
                childs_name.append(child.name)
            if self.auto_release:
                auto_release(parent.name, childs_name)
        else:
            childs_name = {p.name: [] for p in parent_objects}
            if len(scale) == 1:
                scale_f = scale[0]
            else:
                scale_f = [sc[0] for sc in scale]
            for child, parent, sc in zip(child_objects, cycle(parent_objects),
                                         cycle(scale_f)):
                self.set_parent_props(parent, sc)
                self.set_child_props(parent, child)
                childs_name[parent.name].append(child.name)
            if self.auto_release:
                for p in parent_objects:
                    auto_release(p.name, childs_name[p.name])

        self.outputs['Parent'].sv_set(parent_objects)
        self.outputs['Child'].sv_set(child_objects)
Exemple #30
0
class RP_OT_QuickBatch(bpy.types.Operator):
    """ Generate render jobs from scene properties """

    bl_idname = "renderplus.batch_quick"
    bl_label = "Generate Render Jobs"
    bl_options = {"UNDO"}

    prop_type = EnumProperty(
        items=(
            ('QUICK_MARKERS', "Markers", ""),
            ('QUICK_CAMERAS', "Cameras", ""),
            ('QUICK_RLAYERS', "Render Layers", ""),
        ),
        name="Property to use",
    )

    def check_props(self, scene):
        """ Check if the scene has any of the selected property """

        if self.prop_type == 'QUICK_MARKERS':
            return True if len(scene.timeline_markers) > 0 else False

        elif self.prop_type == 'QUICK_RLAYERS':
            return True if len(scene.render.layers) > 0 else False

        elif self.prop_type == 'QUICK_CAMERAS':
            return scene.camera

    def generate_from_markers(self, context, scene):
        """ Generate render jobs from markers """

        markers = list(scene.timeline_markers)
        batch_list = context.scene.renderplus.batch.jobs
        settings = context.scene.renderplus.batch_ops.quick_batch

        # Sort markers by their frame
        markers.sort(key=lambda m: m.frame)

        start_index = len(batch_list) - 1
        index = start_index

        frame_start = 0

        for marker in markers:
            batch_list.add()
            index += 1

            batch_list[index].name = marker.name
            batch_list[index].frame_custom = True
            batch_list[index].frame_still = marker.frame

            # special case, don't use animation
            if marker.frame == 0:
                break

            # Move index to the new job
            context.scene.renderplus.batch.index = index
            batch_list[index].scene = scene.name

            batch_list[index].animation = settings.use_animation
            batch_list[index].frame_start = frame_start
            batch_list[index].frame_end = marker.frame

            frame_start = marker.frame
            
            if settings.no_camera:
                batch_list[index].camera = ''
                
        # Remaining of the animation
        if settings.use_animation and not frame_start == scene.frame_end:
            batch_list.add()
            index += 1

            batch_list[index].name = str.format(
                'Remaining for "{0}"',
                scene.name)
            batch_list[index].frame_custom = True
            batch_list[index].frame_still = scene.frame_end

            context.scene.renderplus.batch.index = index
            batch_list[index].scene = scene.name
            
            if settings.no_camera:
                batch_list[index].camera = ''

            batch_list[index].animation = True
            batch_list[index].frame_start = frame_start
            batch_list[index].frame_end = scene.frame_end


    def generate_from_renderlayers(self, context, scene):
        """ Generate render jobs from render layers """

        batch_list = context.scene.renderplus.batch.jobs
        layers = scene.render.layers

        start_index = len(batch_list) - 1
        index = start_index

        for layer in layers:

            # Only enabled layers
            if layer.use:
                batch_list.add()
                index += 1

                context.scene.renderplus.batch.index = index
                batch_list[index].name = layer.name
                batch_list[index].scene = scene.name
                batch_list[index].layer = layer.name

    def generate_from_cameras(self, context, scene):
        """ Generate render jobs from cameras """

        batch_list = context.scene.renderplus.batch.jobs
        cameras = []

        for obj in scene.objects:
            if obj.type == 'CAMERA':
                cameras.append(obj.name)

        start_index = len(batch_list) - 1
        index = start_index

        for cam in cameras:
            batch_list.add()
            index += 1

            context.scene.renderplus.batch.index = index
            batch_list[index].name = cam
            batch_list[index].scene = scene.name
            batch_list[index].camera = cam

    def execute(self, context):
        scenes = []
        settings = context.scene.renderplus.batch_ops.quick_batch

        if settings.all_scenes:
            for scene in bpy.data.scenes:
                scenes.append(scene)
        elif settings.scene == '':
            scenes.append(context.scene)
        else:
            scenes.append(bpy.data.scenes[settings.scene])

        for scene in scenes:
            # Check we have something to use
            if not self.check_props(scene):
                continue

            if self.prop_type == 'QUICK_MARKERS':
                self.generate_from_markers(context, scene)

            elif self.prop_type == 'QUICK_RLAYERS':
                self.generate_from_renderlayers(context, scene)

            elif self.prop_type == 'QUICK_CAMERAS':
                self.generate_from_cameras(context, scene)

            else:
                log.error('Unkown prop type in autogenerate')

                return {'CANCELLED'}

        if context.scene.renderplus.batch.index == -1:
            context.scene.renderplus.batch.index = 1

        ui.mode = 'NORMAL'
        return {'FINISHED'}