Skip to content
Snippets Groups Projects
utils.py 26.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vilem Duha's avatar
    Vilem Duha committed
    # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    
    from blenderkit import paths, rerequests, image_utils
    
    Vilem Duha's avatar
    Vilem Duha committed
    import bpy
    from mathutils import Vector
    import json
    import os
    
    Vilém Duha's avatar
    Vilém Duha committed
    import shutil
    
    import traceback
    import inspect
    
    bk_logger = logging.getLogger('blenderkit')
    
    ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
    BELOW_NORMAL_PRIORITY_CLASS = 0x00004000
    HIGH_PRIORITY_CLASS = 0x00000080
    IDLE_PRIORITY_CLASS = 0x00000040
    NORMAL_PRIORITY_CLASS = 0x00000020
    REALTIME_PRIORITY_CLASS = 0x00000100
    
    
    def experimental_enabled():
        preferences = bpy.context.preferences.addons['blenderkit'].preferences
        return preferences.experimental_features
    
    
    def get_process_flags():
        flags = BELOW_NORMAL_PRIORITY_CLASS
        if sys.platform != 'win32':  # TODO test this on windows
            flags = 0
        return flags
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    def activate(ob):
        bpy.ops.object.select_all(action='DESELECT')
        ob.select_set(True)
        bpy.context.view_layer.objects.active = ob
    
    
    def selection_get():
    
        aob = bpy.context.view_layer.objects.active
        selobs = bpy.context.view_layer.objects.selected[:]
    
    Vilem Duha's avatar
    Vilem Duha committed
        return (aob, selobs)
    
    
    def selection_set(sel):
        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.view_layer.objects.active = sel[0]
        for ob in sel[1]:
            ob.select_set(True)
    
    
    def get_active_model():
    
        if bpy.context.view_layer.objects.active is not None:
            ob = bpy.context.view_layer.objects.active
    
    Vilem Duha's avatar
    Vilem Duha committed
            while ob.parent is not None:
                ob = ob.parent
            return ob
        return None
    
    
    
    def get_active_HDR():
        scene = bpy.context.scene
        ui_props = scene.blenderkitUI
        image = ui_props.hdr_upload_image
        return image
    
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    def get_selected_models():
    
        '''
        Detect all hierarchies that contain asset data from selection. Only parents that have actual ['asset data'] get returned
        Returns
        list of objects containing asset data.
    
        '''
    
    Vilem Duha's avatar
    Vilem Duha committed
        obs = bpy.context.selected_objects[:]
        done = {}
        parents = []
        for ob in obs:
            if ob not in done:
    
                while ob.parent is not None and ob not in done and ob.blenderkit.asset_base_id == '' and ob.instance_collection is None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                    done[ob] = True
                    ob = ob.parent
    
                if ob not in parents and ob not in done:
    
                    if ob.blenderkit.name != '' or ob.instance_collection is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                        parents.append(ob)
                done[ob] = True
    
        # if no blenderkit - like objects were found, use the original selection.
    
        if len(parents) == 0:
            parents = obs
    
    Vilem Duha's avatar
    Vilem Duha committed
        return parents
    
    
    def get_selected_replace_adepts():
        '''
        Detect all hierarchies that contain either asset data from selection, or selected objects themselves.
        Returns
        list of objects for replacement.
    
        '''
        obs = bpy.context.selected_objects[:]
        done = {}
        parents = []
        for selected_ob in obs:
            ob = selected_ob
            if ob not in done:
                while ob.parent is not None and ob not in done and ob.blenderkit.asset_base_id == '' and ob.instance_collection is None:
                    done[ob] = True
                    # print('step,',ob.name)
                    ob = ob.parent
    
                # print('fin', ob.name)
                if ob not in parents and ob not in done:
                    if ob.blenderkit.name != '' or ob.instance_collection is not None:
                        parents.append(ob)
    
                done[ob] = True
        # print(parents)
    
        # if no blenderkit - like objects were found, use the original selection.
    
        if len(parents) == 0:
            parents = obs
    
    Vilém Duha's avatar
    Vilém Duha committed
        pprint('replace adepts')
        pprint(str(parents))
    
        return parents
    
    Vilem Duha's avatar
    Vilem Duha committed
    def get_search_props():
        scene = bpy.context.scene
    
        if scene is None:
            return;
    
    Vilem Duha's avatar
    Vilem Duha committed
        uiprops = scene.blenderkitUI
        props = None
        if uiprops.asset_type == 'MODEL':
            if not hasattr(scene, 'blenderkit_models'):
                return;
            props = scene.blenderkit_models
        if uiprops.asset_type == 'SCENE':
            if not hasattr(scene, 'blenderkit_scene'):
                return;
            props = scene.blenderkit_scene
    
        if uiprops.asset_type == 'HDR':
            if not hasattr(scene, 'blenderkit_HDR'):
                return;
            props = scene.blenderkit_HDR
    
    Vilem Duha's avatar
    Vilem Duha committed
        if uiprops.asset_type == 'MATERIAL':
            if not hasattr(scene, 'blenderkit_mat'):
                return;
            props = scene.blenderkit_mat
    
        if uiprops.asset_type == 'TEXTURE':
            if not hasattr(scene, 'blenderkit_tex'):
                return;
            # props = scene.blenderkit_tex
    
        if uiprops.asset_type == 'BRUSH':
            if not hasattr(scene, 'blenderkit_brush'):
                return;
            props = scene.blenderkit_brush
        return props
    
    
    def get_active_asset():
        scene = bpy.context.scene
        ui_props = scene.blenderkitUI
        if ui_props.asset_type == 'MODEL':
    
            if bpy.context.view_layer.objects.active is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                ob = get_active_model()
                return ob
        if ui_props.asset_type == 'SCENE':
            return bpy.context.scene
    
        if ui_props.asset_type == 'HDR':
            return get_active_HDR()
    
    Vilem Duha's avatar
    Vilem Duha committed
        elif ui_props.asset_type == 'MATERIAL':
    
            if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                return bpy.context.active_object.active_material
        elif ui_props.asset_type == 'TEXTURE':
            return None
        elif ui_props.asset_type == 'BRUSH':
            b = get_active_brush()
            if b is not None:
                return b
        return None
    
    
    def get_upload_props():
        scene = bpy.context.scene
        ui_props = scene.blenderkitUI
        if ui_props.asset_type == 'MODEL':
    
            if bpy.context.view_layer.objects.active is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                ob = get_active_model()
                return ob.blenderkit
        if ui_props.asset_type == 'SCENE':
            s = bpy.context.scene
            return s.blenderkit
    
        if ui_props.asset_type == 'HDR':
    
            hdr = ui_props.hdr_upload_image#bpy.data.images.get(ui_props.hdr_upload_image)
            if not hdr:
                return None
            return hdr.blenderkit
    
    Vilem Duha's avatar
    Vilem Duha committed
        elif ui_props.asset_type == 'MATERIAL':
    
            if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                return bpy.context.active_object.active_material.blenderkit
        elif ui_props.asset_type == 'TEXTURE':
            return None
        elif ui_props.asset_type == 'BRUSH':
            b = get_active_brush()
            if b is not None:
                return b.blenderkit
        return None
    
    
    def previmg_name(index, fullsize=False):
        if not fullsize:
    
    Vilém Duha's avatar
    Vilém Duha committed
            return '.bkit_preview_' + str(index).zfill(3)
    
    Vilem Duha's avatar
    Vilem Duha committed
        else:
    
    Vilém Duha's avatar
    Vilém Duha committed
            return '.bkit_preview_full_' + str(index).zfill(3)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    def get_active_brush():
        context = bpy.context
        brush = None
        if context.sculpt_object:
            brush = context.tool_settings.sculpt.brush
        elif context.image_paint_object:  # could be just else, but for future possible more types...
            brush = context.tool_settings.image_paint.brush
        return brush
    
    
    def load_prefs():
        user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
        # if user_preferences.api_key == '':
        fpath = paths.BLENDERKIT_SETTINGS_FILENAME
        if os.path.exists(fpath):
    
                with open(fpath, 'r', encoding = 'utf-8') as s:
    
                    prefs = json.load(s)
                    user_preferences.api_key = prefs.get('API_key', '')
                    user_preferences.global_dir = prefs.get('global_dir', paths.default_global_dict())
                    user_preferences.api_key_refresh = prefs.get('API_key_refresh', '')
            except Exception as e:
                print('failed to read addon preferences.')
                print(e)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    def save_prefs(self, context):
    
        # first check context, so we don't do this on registration or blender startup
    
        if not bpy.app.background:  # (hasattr kills blender)
    
            user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
    
            # we test the api key for length, so not a random accidentally typed sequence gets saved.
    
            lk = len(user_preferences.api_key)
            if 0 < lk < 25:
                # reset the api key in case the user writes some nonsense, e.g. a search string instead of the Key
                user_preferences.api_key = ''
                props = get_search_props()
                props.report = 'Login failed. Please paste a correct API Key.'
    
            prefs = {
                'API_key': user_preferences.api_key,
                'API_key_refresh': user_preferences.api_key_refresh,
                'global_dir': user_preferences.global_dir,
            }
    
            try:
                fpath = paths.BLENDERKIT_SETTINGS_FILENAME
                if not os.path.exists(paths._presets):
                    os.makedirs(paths._presets)
    
                with open(fpath, 'w', encoding = 'utf-8') as s:
                    json.dump(prefs, s, ensure_ascii=False, indent=4)
    
            except Exception as e:
                print(e)
    
    def uploadable_asset_poll():
        '''returns true if active asset type can be uploaded'''
        ui_props = bpy.context.scene.blenderkitUI
        if ui_props.asset_type == 'MODEL':
            return bpy.context.view_layer.objects.active is not None
        if ui_props.asset_type == 'MATERIAL':
            return bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None
        if ui_props.asset_type == 'HDR':
            return ui_props.hdr_upload_image is not None
        return True
    
    
    def get_hidden_texture(img, force_reload=False):
        # i = get_hidden_image(tpath, bdata_name, force_reload=force_reload)
        # bdata_name = f".{bdata_name}"
        t = bpy.data.textures.get(img.name)
    
        if t is None:
    
            t = bpy.data.textures.new(img.name, 'IMAGE')
        if t.image != img:
            t.image = img
    
    def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace = 'sRGB'):
        if bdata_name[0] == '.':
            hidden_name = bdata_name
        else:
            hidden_name = '.%s' % bdata_name
    
    Vilem Duha's avatar
    Vilem Duha committed
        img = bpy.data.images.get(hidden_name)
    
        if tpath.startswith('//'):
            tpath = bpy.path.abspath(tpath)
    
        if img == None or (img.filepath != tpath):
            if tpath.startswith('//'):
                tpath = bpy.path.abspath(tpath)
    
            if not os.path.exists(tpath) or os.path.isdir(tpath):
    
    Vilem Duha's avatar
    Vilem Duha committed
                tpath = paths.get_addon_thumbnail_path('thumbnail_notready.jpg')
    
            if img is None:
                img = bpy.data.images.load(tpath)
                img.name = hidden_name
            else:
                if img.filepath != tpath:
                    if img.packed_file is not None:
                        img.unpack(method='USE_ORIGINAL')
    
                    img.filepath = tpath
                    img.reload()
    
            image_utils.set_colorspace(img,colorspace)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        elif force_reload:
            if img.packed_file is not None:
                img.unpack(method='USE_ORIGINAL')
            img.reload()
    
            image_utils.set_colorspace(img,colorspace)
    
    Vilem Duha's avatar
    Vilem Duha committed
        return img
    
    
    def get_thumbnail(name):
        p = paths.get_addon_thumbnail_path(name)
        name = '.%s' % name
        img = bpy.data.images.get(name)
        if img == None:
            img = bpy.data.images.load(p)
    
            image_utils.set_colorspace(img,'sRGB')
    
    Vilem Duha's avatar
    Vilem Duha committed
            img.name = name
            img.name = name
    
        return img
    
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    def files_size_to_text(size):
        fsmb = size // (1024 * 1024)
        fskb = size % 1024
        if fsmb == 0:
            return f'{fskb}KB'
        else:
            return f'{fsmb}MB {fskb}KB'
    
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    def get_brush_props(context):
        brush = get_active_brush()
        if brush is not None:
            return brush.blenderkit
        return None
    
    
    def p(text, text1='', text2='', text3='', text4='', text5='', level = 'DEBUG'):
    
        '''debug printing depending on blender's debug value'''
    
        if 1:#bpy.app.debug_value != 0:
    
    Vilém Duha's avatar
    Vilém Duha committed
            # print('-----BKit debug-----\n')
            # traceback.print_stack()
    
            texts = [text1,text2,text3,text4,text5]
            text = str(text)
            for t in texts:
                if t!= '':
                    text += ' ' + str(t)
    
            bk_logger.debug(text)
    
    Vilém Duha's avatar
    Vilém Duha committed
            # print('---------------------\n')
    
    
    def copy_asset(fp1, fp2):
        '''synchronizes the asset between folders, including it's texture subdirectories'''
        if 1:
    
            bk_logger.debug('copy asset')
    
    Vilém Duha's avatar
    Vilém Duha committed
            bk_logger.debug(fp1 +' '+ fp2)
    
    Vilém Duha's avatar
    Vilém Duha committed
            if not os.path.exists(fp2):
                shutil.copyfile(fp1, fp2)
    
                bk_logger.debug('copied')
    
    Vilém Duha's avatar
    Vilém Duha committed
            source_dir = os.path.dirname(fp1)
            target_dir = os.path.dirname(fp2)
            for subdir in os.scandir(source_dir):
                if not subdir.is_dir():
                    continue
                target_subdir = os.path.join(target_dir, subdir.name)
                if os.path.exists(target_subdir):
                    continue
    
                bk_logger.debug(str(subdir) +' '+ str(target_subdir))
    
    Vilém Duha's avatar
    Vilém Duha committed
                shutil.copytree(subdir, target_subdir)
    
                bk_logger.debug('copied')
    
    Vilém Duha's avatar
    Vilém Duha committed
    
        # except Exception as e:
        #     print('BlenderKit failed to copy asset')
        #     print(fp1, fp2)
        #     print(e)
    
    
    def pprint(data, data1=None, data2=None, data3=None, data4=None):
    
        '''pretty print jsons'''
    
        p(json.dumps(data, indent=4, sort_keys=True))
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    def get_hierarchy(ob):
    
        '''get all objects in a tree'''
    
    Vilem Duha's avatar
    Vilem Duha committed
        obs = []
        doobs = [ob]
    
    Vilém Duha's avatar
    Vilém Duha committed
        # pprint('get hierarchy')
        pprint(ob.name)
    
    Vilem Duha's avatar
    Vilem Duha committed
        while len(doobs) > 0:
            o = doobs.pop()
            doobs.extend(o.children)
            obs.append(o)
        return obs
    
    
    
    def select_hierarchy(ob, state=True):
    
        obs = get_hierarchy(ob)
        for ob in obs:
            ob.select_set(state)
        return obs
    
    
    def delete_hierarchy(ob):
        obs = get_hierarchy(ob)
        bpy.ops.object.delete({"selected_objects": obs})
    
    Vilem Duha's avatar
    Vilem Duha committed
    def get_bounds_snappable(obs, use_modifiers=False):
        # progress('getting bounds of object(s)')
        parent = obs[0]
        while parent.parent is not None:
            parent = parent.parent
        maxx = maxy = maxz = -10000000
        minx = miny = minz = 10000000
    
        s = bpy.context.scene
    
        obcount = 0  # calculates the mesh obs. Good for non-mesh objects
        matrix_parent = parent.matrix_world
        for ob in obs:
            # bb=ob.bound_box
            mw = ob.matrix_world
            subp = ob.parent
            # while parent.parent is not None:
            #     mw =
    
            if ob.type == 'MESH' or ob.type == 'CURVE':
                # If to_mesh() works we can use it on curves and any other ob type almost.
                # disabled to_mesh for 2.8 by now, not wanting to use dependency graph yet.
    
                depsgraph = bpy.context.evaluated_depsgraph_get()
    
                object_eval = ob.evaluated_get(depsgraph)
    
                if ob.type == 'CURVE':
                    mesh = object_eval.to_mesh()
                else:
                    mesh = object_eval.data
    
                # to_mesh(context.depsgraph, apply_modifiers=self.applyModifiers, calc_undeformed=False)
    
    Vilem Duha's avatar
    Vilem Duha committed
                obcount += 1
    
    Vilém Duha's avatar
    Vilém Duha committed
                if mesh is not None:
                    for c in mesh.vertices:
                        coord = c.co
                        parent_coord = matrix_parent.inverted() @ mw @ Vector(
                            (coord[0], coord[1], coord[2]))  # copy this when it works below.
                        minx = min(minx, parent_coord.x)
                        miny = min(miny, parent_coord.y)
                        minz = min(minz, parent_coord.z)
                        maxx = max(maxx, parent_coord.x)
                        maxy = max(maxy, parent_coord.y)
                        maxz = max(maxz, parent_coord.z)
                    # bpy.data.meshes.remove(mesh)
    
                if ob.type == 'CURVE':
                    object_eval.to_mesh_clear()
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        if obcount == 0:
            minx, miny, minz, maxx, maxy, maxz = 0, 0, 0, 0, 0, 0
    
        minx *= parent.scale.x
        maxx *= parent.scale.x
        miny *= parent.scale.y
        maxy *= parent.scale.y
        minz *= parent.scale.z
        maxz *= parent.scale.z
    
        return minx, miny, minz, maxx, maxy, maxz
    
    
    def get_bounds_worldspace(obs, use_modifiers=False):
        # progress('getting bounds of object(s)')
        s = bpy.context.scene
        maxx = maxy = maxz = -10000000
        minx = miny = minz = 10000000
        obcount = 0  # calculates the mesh obs. Good for non-mesh objects
        for ob in obs:
            # bb=ob.bound_box
            mw = ob.matrix_world
            if ob.type == 'MESH' or ob.type == 'CURVE':
    
                depsgraph = bpy.context.evaluated_depsgraph_get()
    
                ob_eval = ob.evaluated_get(depsgraph)
                mesh = ob_eval.to_mesh()
    
    Vilem Duha's avatar
    Vilem Duha committed
                obcount += 1
    
    Vilém Duha's avatar
    Vilém Duha committed
                if mesh is not None:
                    for c in mesh.vertices:
                        coord = c.co
                        world_coord = mw @ Vector((coord[0], coord[1], coord[2]))
                        minx = min(minx, world_coord.x)
                        miny = min(miny, world_coord.y)
                        minz = min(minz, world_coord.z)
                        maxx = max(maxx, world_coord.x)
                        maxy = max(maxy, world_coord.y)
                        maxz = max(maxz, world_coord.z)
    
                ob_eval.to_mesh_clear()
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        if obcount == 0:
            minx, miny, minz, maxx, maxy, maxz = 0, 0, 0, 0, 0, 0
        return minx, miny, minz, maxx, maxy, maxz
    
    
    def is_linked_asset(ob):
        return ob.get('asset_data') and ob.instance_collection != None
    
    
    def get_dimensions(obs):
        minx, miny, minz, maxx, maxy, maxz = get_bounds_snappable(obs)
        bbmin = Vector((minx, miny, minz))
        bbmax = Vector((maxx, maxy, maxz))
        dim = Vector((maxx - minx, maxy - miny, maxz - minz))
        return dim, bbmin, bbmax
    
    
    def requests_post_thread(url, json, headers):
    
        r = rerequests.post(url, json=json, verify=True, headers=headers)
    
    def get_headers(api_key):
        headers = {
            "accept": "application/json",
        }
        if api_key != '':
            headers["Authorization"] = "Bearer %s" % api_key
        return headers
    
    
    def scale_2d(v, s, p):
        '''scale a 2d vector with a pivot'''
        return (p[0] + s[0] * (v[0] - p[0]), p[1] + s[1] * (v[1] - p[1]))
    
    
    
    def scale_uvs(ob, scale=1.0, pivot=Vector((.5, .5))):
    
        if len(mesh.uv_layers) > 0:
    
            uv = mesh.uv_layers[mesh.uv_layers.active_index]
    
            # Scale a UV map iterating over its coordinates to a given scale and with a pivot point
            for uvindex in range(len(uv.data)):
                uv.data[uvindex].uv = scale_2d(uv.data[uvindex].uv, scale, pivot)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    # map uv cubic and switch of auto tex space and set it to 1,1,1
    
    def automap(target_object=None, target_slot=None, tex_size=1, bg_exception=False, just_scale=False):
    
    Vilem Duha's avatar
    Vilem Duha committed
        s = bpy.context.scene
        mat_props = s.blenderkit_mat
        if mat_props.automap:
            tob = bpy.data.objects[target_object]
            # only automap mesh models
    
    Vilém Duha's avatar
    Vilém Duha committed
            if tob.type == 'MESH' and len(tob.data.polygons) > 0:
                # check polycount for a rare case where no polys are in editmesh
    
    Vilem Duha's avatar
    Vilem Duha committed
                actob = bpy.context.active_object
                bpy.context.view_layer.objects.active = tob
    
                # auto tex space
                if tob.data.use_auto_texspace:
                    tob.data.use_auto_texspace = False
    
    
                if not just_scale:
                    tob.data.texspace_size = (1, 1, 1)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
                if 'automap' not in tob.data.uv_layers:
                    bpy.ops.mesh.uv_texture_add()
                    uvl = tob.data.uv_layers[-1]
                    uvl.name = 'automap'
    
                # TODO limit this to active material
                # tob.data.uv_textures['automap'].active = True
    
                scale = tob.scale.copy()
    
                if target_slot is not None:
                    tob.active_material_index = target_slot
                bpy.ops.object.mode_set(mode='EDIT')
                bpy.ops.mesh.select_all(action='DESELECT')
    
                # this exception is just for a 2.8 background thunmbnailer crash, can be removed when material slot select works...
                if bg_exception:
                    bpy.ops.mesh.select_all(action='SELECT')
                else:
                    bpy.ops.object.material_slot_select()
    
                scale = (scale.x + scale.y + scale.z) / 3.0
    
                if not just_scale:
                    bpy.ops.uv.cube_project(
                        cube_size=scale * 2.0 / (tex_size),
                        correct_aspect=False)  # it's * 2.0 because blender can't tell size of a unit cube :)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
                bpy.ops.object.editmode_toggle()
                tob.data.uv_layers.active = tob.data.uv_layers['automap']
    
                tob.data.uv_layers["automap"].active_render = True
    
                # this by now works only for thumbnail preview, but should be extended to work on arbitrary objects.
                # by now, it takes the basic uv map = 1 meter. also, it now doeasn't respect more materials on one object,
                # it just scales whole UV.
                if just_scale:
    
                    scale_uvs(tob, scale=Vector((1 / tex_size, 1 / tex_size)))
    
    Vilem Duha's avatar
    Vilem Duha committed
                bpy.context.view_layer.objects.active = actob
    
        scene = bpy.context.scene
        ui_props = scene.blenderkitUI
    
    
        props = get_upload_props()
        if props.name_old != props.name:
            props.name_changed = True
            props.name_old = props.name
            nname = props.name.strip()
            nname = nname.replace('_', ' ')
    
            if nname.isupper():
                nname = nname.lower()
            nname = nname[0].upper() + nname[1:]
            props.name = nname
            # here we need to fix the name for blender data = ' or " give problems in path evaluation down the road.
        fname = props.name
        fname = fname.replace('\'', '')
        fname = fname.replace('\"', '')
        asset = get_active_asset()
    
        if ui_props.asset_type != 'HDR':
            # Here we actually rename assets datablocks, but don't do that with HDR's and possibly with others
            asset.name = fname
    
    Vilém Duha's avatar
    Vilém Duha committed
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    def get_param(asset_data, parameter_name):
        if not asset_data.get('parameters'):
            # this can appear in older version files.
            return None
    
        for p in asset_data['parameters']:
            if p.get('parameterType') == parameter_name:
                return p['value']
        return None
    
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    def params_to_dict(params):
        params_dict = {}
        for p in params:
            params_dict[p['parameterType']] = p['value']
        return params_dict
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    def dict_to_params(inputs, parameters=None):
        if parameters == None:
            parameters = []
        for k in inputs.keys():
            if type(inputs[k]) == list:
                strlist = ""
                for idx, s in enumerate(inputs[k]):
                    strlist += s
                    if idx < len(inputs[k]) - 1:
                        strlist += ','
    
                value = "%s" % strlist
            elif type(inputs[k]) != bool:
                value = inputs[k]
            else:
                value = str(inputs[k])
            parameters.append(
                {
                    "parameterType": k,
                    "value": value
                })
        return parameters
    
    def update_tags(self, context):
        props = self
    
        commasep = props.tags.split(',')
        ntags = []
        for tag in commasep:
            if len(tag) > 19:
                short_tags = tag.split(' ')
                for short_tag in short_tags:
                    if len(short_tag) > 19:
                        short_tag = short_tag[:18]
                    ntags.append(short_tag)
            else:
                ntags.append(tag)
        if len(ntags) == 1:
            ntags = ntags[0].split(' ')
        ns = ''
        for t in ntags:
            if t != '':
                ns += t + ','
        ns = ns[:-1]
        if props.tags != ns:
            props.tags = ns
    
    Vilém Duha's avatar
    Vilém Duha committed
    def user_logged_in():
        a = bpy.context.window_manager.get('bkit profile')
        if a is not None:
            return True
        return False
    
    
    
    def profile_is_validator():
        a = bpy.context.window_manager.get('bkit profile')
        if a is not None and a['user'].get('exmenu'):
            return True
    
    def guard_from_crash():
        '''Blender tends to crash when trying to run some functions with the addon going through unregistration process.'''
    
        if bpy.context.preferences.addons.get('blenderkit') is None:
    
            return False;
        if bpy.context.preferences.addons['blenderkit'].preferences is None:
            return False;
    
        return True
    
    def get_largest_area(area_type='VIEW_3D'):
    
        maxsurf = 0
        maxa = None
        maxw = None
        region = None
    
    Vilém Duha's avatar
    Vilém Duha committed
        for w in bpy.data.window_managers[0].windows:
    
            for a in w.screen.areas:
                if a.type == area_type:
    
                    asurf = a.width * a.height
                    if asurf > maxsurf:
                        maxa = a
                        maxw = w
                        maxsurf = asurf
    
                        for r in a.regions:
                            if r.type == 'WINDOW':
                                region = r
    
        global active_area_pointer, active_window_pointer, active_region_pointer
        active_window_pointer = maxw.as_pointer()
        active_area_pointer = maxa.as_pointer()
        active_region_pointer = region.as_pointer()
    
        return maxw, maxa, region
    
    
    
    def get_fake_context(context, area_type='VIEW_3D'):
        C_dict = {}  # context.copy() #context.copy was a source of problems - incompatibility with addons that also define context
    
        C_dict.update(region='WINDOW')
    
        # try:
        #     context = context.copy()
        #     # print('bk context copied successfully')
        # except Exception as e:
        #     print(e)
        #     print('BlenderKit: context.copy() failed. Can be a colliding addon.')
        context = {}
    
    
        if context.get('area') is None or context.get('area').type != area_type:
    
            w, a, r = get_largest_area(area_type=area_type)
    
    Vilém Duha's avatar
    Vilém Duha committed
                # sometimes there is no area of the requested type. Let's face it, some people use Blender without 3d view.
    
                override = {'window': w, 'screen': w.screen, 'area': a, 'region': r}
                C_dict.update(override)
    
            # print(w,a,r)
    
    def label_multiline(layout, text='', icon='NONE', width=-1):
        ''' draw a ui label, but try to split it in multiple lines.'''
        if text.strip() == '':
            return
        lines = text.split('\n')
        if width > 0:
            threshold = int(width / 5.5)
        else:
            threshold = 35
        maxlines = 8
        li = 0
        for l in lines:
            while len(l) > threshold:
                i = l.rfind(' ', 0, threshold)
                if i < 1:
                    i = threshold
                l1 = l[:i]
                layout.label(text=l1, icon=icon)
                icon = 'NONE'
                l = l[i:].lstrip()
                li += 1
                if li > maxlines:
                    break;
            if li > maxlines:
                break;
            layout.label(text=l, icon=icon)
    
        traceback.print_stack()