Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'blenderkit/ui_panels.py')
-rw-r--r--blenderkit/ui_panels.py2681
1 files changed, 0 insertions, 2681 deletions
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
deleted file mode 100644
index c3734554..00000000
--- a/blenderkit/ui_panels.py
+++ /dev/null
@@ -1,2681 +0,0 @@
-# ##### 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, comments_utils, ratings, ratings_utils, utils, download, categories, icons, search, \
- resolutions, ui, \
- tasks_queue, \
- autothumb, upload
-
-from bpy.types import (
- Panel
-)
-from bpy.props import (
- IntProperty,
- FloatProperty,
- FloatVectorProperty,
- StringProperty,
- EnumProperty,
- BoolProperty,
- PointerProperty,
-)
-
-import bpy
-import os
-import random
-import logging
-import platform
-import ctypes
-
-bk_logger = logging.getLogger('blenderkit')
-
-
-# this was moved to separate interface:
-
-def draw_ratings(layout, context, asset):
- # layout.operator("wm.url_open", text="Read rating instructions", icon='QUESTION').url = 'https://support.google.com/?hl=en'
- # the following shouldn't happen at all in an optimal case,
- # this function should run only when asset was already checked to be existing
- if asset == None:
- return;
-
- col = layout.column()
- bkit_ratings = asset.bkit_ratings
-
- # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
-
- row = col.row()
- row.prop(bkit_ratings, 'rating_quality_ui', expand=True, icon_only=True, emboss=False)
- if bkit_ratings.rating_quality > 0:
- col.separator()
- col.prop(bkit_ratings, 'rating_work_hours')
- # w = context.region.width
-
- # layout.label(text='problems')
- # layout.prop(bkit_ratings, 'rating_problems', text='')
- # layout.label(text='compliments')
- # layout.prop(bkit_ratings, 'rating_compliments', text='')
-
- # row = layout.row()
- # op = row.operator("object.blenderkit_rating_upload", text="Send rating", icon='URL')
- # return op
- # re-enable layout if included in longer panel
-
-
-def draw_not_logged_in(source, message='Please Login/Signup to use this feature'):
- title = "You aren't logged in"
-
- def draw_message(source, context):
- layout = source.layout
- utils.label_multiline(layout, text=message)
- draw_login_buttons(layout)
-
- bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
-
-
-def draw_upload_common(layout, props, asset_type, context):
- op = layout.operator("wm.url_open", text=f"Read {asset_type.lower()} upload instructions",
- icon='QUESTION')
- if asset_type == 'MODEL':
- op.url = paths.BLENDERKIT_MODEL_UPLOAD_INSTRUCTIONS_URL
- if asset_type == 'MATERIAL':
- op.url = paths.BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL
- if asset_type == 'BRUSH':
- op.url = paths.BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL
- if asset_type == 'SCENE':
- op.url = paths.BLENDERKIT_SCENE_UPLOAD_INSTRUCTIONS_URL
- if asset_type == 'HDR':
- op.url = paths.BLENDERKIT_HDR_UPLOAD_INSTRUCTIONS_URL
-
- row = layout.row(align=True)
- if props.upload_state != '':
- utils.label_multiline(layout, text=props.upload_state, width=context.region.width)
- if props.uploading:
- op = layout.operator('object.kill_bg_process', text="", icon='CANCEL')
- op.process_source = asset_type
- op.process_type = 'UPLOAD'
- layout = layout.column()
- layout.enabled = False
- # if props.upload_state.find('Error') > -1:
- # layout.label(text = props.upload_state)
-
- if props.asset_base_id == '':
- optext = 'Upload %s' % asset_type.lower()
- op = layout.operator("object.blenderkit_upload", text=optext, icon='EXPORT')
- op.asset_type = asset_type
- op.reupload = False
- # make sure everything gets uploaded.
- op.main_file = True
- op.metadata = True
- op.thumbnail = True
-
- if props.asset_base_id != '':
- op = layout.operator("object.blenderkit_upload", text='Reupload asset', icon='EXPORT')
- op.asset_type = asset_type
- op.reupload = True
-
- op = layout.operator("object.blenderkit_upload", text='Upload as new asset', icon='EXPORT')
- op.asset_type = asset_type
- op.reupload = False
-
- # layout.label(text = 'asset id, overwrite only for reuploading')
- layout.label(text='asset has a version online.')
- # row = layout.row()
- # row.enabled = False
- # row.prop(props, 'asset_base_id', icon='FILE_TICK')
- # row = layout.row()
- # row.enabled = False
- # row.prop(props, 'id', icon='FILE_TICK')
- layout.prop(props, 'category')
- if props.category != 'NONE' and props.subcategory != 'NONE':
- layout.prop(props, 'subcategory')
- if props.subcategory != 'NONE' and props.subcategory1 != 'NONE':
- layout.prop(props, 'subcategory1')
-
- layout.prop(props, 'is_private', expand=True)
- if props.is_private == 'PUBLIC':
- layout.prop(props, 'license')
- layout.prop(props, 'is_free', expand=True)
-
- prop_needed(layout, props, 'name', props.name)
- if props.is_private == 'PUBLIC':
- prop_needed(layout, props, 'description', props.description)
- prop_needed(layout, props, 'tags', props.tags)
- else:
- layout.prop(props, 'description')
- layout.prop(props, 'tags')
-
-
-def poll_local_panels():
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- return user_preferences.panel_behaviour == 'BOTH' or user_preferences.panel_behaviour == 'LOCAL'
-
-
-def prop_needed(layout, props, name, value='', is_not_filled=''):
- row = layout.row()
- if value == is_not_filled:
- # row.label(text='', icon = 'ERROR')
- icon = 'ERROR'
- row.alert = True
- row.prop(props, name) # , icon=icon)
- row.alert = False
- else:
- # row.label(text='', icon = 'FILE_TICK')
- icon = None
- row.prop(props, name)
-
-
-def draw_panel_hdr_upload(self, context):
- layout = self.layout
- ui_props = bpy.context.window_manager.blenderkitUI
-
- # layout.prop_search(ui_props, "hdr_upload_image", bpy.data, "images")
- layout.prop(ui_props, "hdr_upload_image")
-
- hdr = utils.get_active_HDR()
-
- if hdr is not None:
- props = hdr.blenderkit
-
- layout = self.layout
-
- draw_upload_common(layout, props, 'HDR', context)
-
-
-def draw_panel_hdr_search(self, context):
- s = context.scene
- wm = context.window_manager
- props = wm.blenderkit_HDR
-
- layout = self.layout
- row = layout.row()
- row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
- draw_assetbar_show_hide(row, props)
- layout.prop(props, "own_only")
-
- utils.label_multiline(layout, text=props.report)
-
-
-def draw_thumbnail_upload_panel(layout, props):
- update = False
- tex = autothumb.get_texture_ui(props.thumbnail, 'upload_preview')
- if not tex or not tex.image:
- return
- box = layout.box()
- box.template_icon(icon_value=tex.image.preview.icon_id, scale=6.0)
-
-
-def draw_panel_model_upload(self, context):
- ob = bpy.context.active_object
- while ob.parent is not None:
- ob = ob.parent
- props = ob.blenderkit
-
- layout = self.layout
-
- draw_upload_common(layout, props, 'MODEL', context)
-
- col = layout.column()
- if props.is_generating_thumbnail:
- col.enabled = False
-
- draw_thumbnail_upload_panel(col, props)
-
- prop_needed(col, props, 'thumbnail', props.thumbnail)
- if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'):
- col.operator("object.blenderkit_generate_thumbnail", text='Generate thumbnail', icon='IMAGE')
-
- # row = layout.row(align=True)
- if props.is_generating_thumbnail:
- row = layout.row(align=True)
- row.label(text=props.thumbnail_generating_state)
- op = row.operator('object.kill_bg_process', text="", icon='CANCEL')
- op.process_source = 'MODEL'
- op.process_type = 'THUMBNAILER'
- elif props.thumbnail_generating_state != '':
- utils.label_multiline(layout, text=props.thumbnail_generating_state)
-
- # prop_needed(layout, props, 'style', props.style)
- # prop_needed(layout, props, 'production_level', props.production_level)
- layout.prop(props, 'style')
- layout.prop(props, 'production_level')
-
- layout.prop(props, 'condition')
- layout.prop(props, 'pbr')
- layout.label(text='design props:')
- layout.prop(props, 'manufacturer')
- layout.prop(props, 'designer')
- layout.prop(props, 'design_collection')
- layout.prop(props, 'design_variant')
- layout.prop(props, 'use_design_year')
- if props.use_design_year:
- layout.prop(props, 'design_year')
-
- row = layout.row()
- row.prop(props, 'work_hours')
-
- layout.prop(props, 'adult')
-
-
-def draw_panel_scene_upload(self, context):
- s = bpy.context.scene
- props = s.blenderkit
-
- layout = self.layout
- # if bpy.app.debug_value != -1:
- # layout.label(text='Scene upload not Implemented')
- # return
- draw_upload_common(layout, props, 'SCENE', context)
-
- # layout = layout.column()
-
- # row = layout.row()
-
- # if props.dimensions[0] + props.dimensions[1] == 0 and props.face_count == 0:
- # icon = 'ERROR'
- # layout.operator("object.blenderkit_auto_tags", text='Auto fill tags', icon=icon)
- # else:
- # layout.operator("object.blenderkit_auto_tags", text='Auto fill tags')
-
- col = layout.column()
- # if props.is_generating_thumbnail:
- # col.enabled = False
- draw_thumbnail_upload_panel(col, props)
-
- prop_needed(col, props, 'thumbnail', props.has_thumbnail, False)
- # if bpy.context.scene.render.engine == 'CYCLES':
- # col.operator("object.blenderkit_generate_thumbnail", text='Generate thumbnail', icon='IMAGE_COL')
-
- # row = layout.row(align=True)
- # if props.is_generating_thumbnail:
- # row = layout.row(align=True)
- # row.label(text = props.thumbnail_generating_state)
- # op = row.operator('object.kill_bg_process', text="", icon='CANCEL')
- # op.process_source = 'MODEL'
- # op.process_type = 'THUMBNAILER'
- # elif props.thumbnail_generating_state != '':
- # utils.label_multiline(layout, text = props.thumbnail_generating_state)
-
- layout.prop(props, 'style')
- layout.prop(props, 'production_level')
- layout.prop(props, 'use_design_year')
- if props.use_design_year:
- layout.prop(props, 'design_year')
- layout.prop(props, 'condition')
- row = layout.row()
- row.prop(props, 'work_hours')
- layout.prop(props, 'adult')
-
-
-def draw_assetbar_show_hide(layout, props):
- s = bpy.context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
-
- if ui_props.assetbar_on:
- icon = 'HIDE_OFF'
- ttip = 'Click to Hide Asset Bar'
- else:
- icon = 'HIDE_ON'
- ttip = 'Click to Show Asset Bar'
-
- preferences = bpy.context.preferences.addons['blenderkit'].preferences
- if 1:#preferences.experimental_features:
- op = layout.operator('view3d.blenderkit_asset_bar_widget', text='', icon=icon)
- else:
- op = layout.operator('view3d.blenderkit_asset_bar', text='', icon=icon)
- op.keep_running = False
- op.do_search = False
- op.tooltip = ttip
-
-
-def draw_panel_model_search(self, context):
- wm = bpy.context.window_manager
- props = wm.blenderkit_models
-
- layout = self.layout
-
- row = layout.row()
- row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
- draw_assetbar_show_hide(row, props)
-
- icon = 'NONE'
- if props.report == 'You need Full plan to get this item.':
- icon = 'ERROR'
- utils.label_multiline(layout, text=props.report, icon=icon)
- if props.report == 'You need Full plan to get this item.':
- layout.operator("wm.url_open", text="Get Full plan", icon='URL').url = paths.BLENDERKIT_PLANS
-
- # layout.prop(props, "search_style")
- # layout.prop(props, "own_only")
- # layout.prop(props, "free_only")
-
- # if props.search_style == 'OTHER':
- # layout.prop(props, "search_style_other")
- # layout.prop(props, "search_engine")
- # col = layout.column()
- # layout.prop(props, 'append_link', expand=True, icon_only=False)
- # layout.prop(props, 'import_as', expand=True, icon_only=False)
-
- # draw_panel_categories(self, context)
-
-
-def draw_panel_scene_search(self, context):
- wm = bpy.context.window_manager
- props = wm.blenderkit_scene
- layout = self.layout
- # layout.label(text = "common search properties:")
- row = layout.row()
- row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
- draw_assetbar_show_hide(row, props)
- layout.prop(props, "own_only")
- utils.label_multiline(layout, text=props.report)
-
- # layout.prop(props, "search_style")
- # if props.search_style == 'OTHER':
- # layout.prop(props, "search_style_other")
- # layout.prop(props, "search_engine")
- layout.separator()
- # draw_panel_categories(self, context)
-
-
-class VIEW3D_PT_blenderkit_model_properties(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_model_properties"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Selected Model"
- bl_context = "objectmode"
-
- @classmethod
- def poll(cls, context):
- p = bpy.context.view_layer.objects.active is not None
- return p
-
- def draw(self, context):
- # draw asset properties here
- layout = self.layout
-
- o = utils.get_active_model()
- # o = bpy.context.active_object
- if o.get('asset_data') is None:
- utils.label_multiline(layout,
- text='To upload this asset to BlenderKit, go to the Find and Upload Assets panel.')
- layout.prop(o, 'name')
-
- if o.get('asset_data') is not None:
- ad = o['asset_data']
- layout.label(text=str(ad['name']))
- if o.instance_type == 'COLLECTION' and o.instance_collection is not None:
- layout.operator('object.blenderkit_bring_to_scene', text='Bring to scene')
-
- layout.label(text='Asset tools:')
- draw_asset_context_menu(self.layout, context, ad, from_panel=True)
- # if 'rig' in ad['tags']:
- # # layout.label(text = 'can make proxy')
- # layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
- # fast upload, blocked by now
- # else:
- # op = layout.operator("object.blenderkit_upload", text='Store as private', icon='EXPORT')
- # op.asset_type = 'MODEL'
- # op.fast = True
- # fun override project, not finished
- # layout.operator('object.blenderkit_color_corrector')
-
-
-class NODE_PT_blenderkit_material_properties(Panel):
- bl_category = "BlenderKit"
- bl_idname = "NODE_PT_blenderkit_material_properties"
- bl_space_type = 'NODE_EDITOR'
- bl_region_type = 'UI'
- bl_label = "Selected Material"
- bl_context = "objectmode"
-
- @classmethod
- def poll(cls, context):
- p = bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None
- return p
-
- def draw(self, context):
- # draw asset properties here
- layout = self.layout
-
- m = bpy.context.active_object.active_material
- # o = bpy.context.active_object
- if m.get('asset_data') is None and m.blenderkit.id == '':
- utils.label_multiline(layout,
- text='To upload this asset to BlenderKit, go to the Find and Upload Assets panel.')
- layout.prop(m, 'name')
-
- if m.get('asset_data') is not None:
- ad = m['asset_data']
- layout.label(text=str(ad['name']))
-
- layout.label(text='Asset tools:')
- draw_asset_context_menu(self.layout, context, ad, from_panel=True)
- # if 'rig' in ad['tags']:
- # # layout.label(text = 'can make proxy')
- # layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
- # fast upload, blocked by now
- # else:
- # op = layout.operator("object.blenderkit_upload", text='Store as private', icon='EXPORT')
- # op.asset_type = 'MODEL'
- # op.fast = True
- # fun override project, not finished
- # layout.operator('object.blenderkit_color_corrector')
-
-
-def draw_rating_asset(self, context, asset):
- layout = self.layout
- col = layout.box()
- # split = layout.split(factor=0.5)
- # col1 = split.column()
- # col2 = split.column()
- # print('%s_search' % asset['asset_data']['assetType'])
- directory = paths.get_temp_dir('%s_search' % asset['asset_data']['assetType'])
- tpath = os.path.join(directory, asset['asset_data']['thumbnail_small'])
- for image in bpy.data.images:
- if image.filepath == tpath:
- # split = row.split(factor=1.0, align=False)
- col.template_icon(icon_value=image.preview.icon_id, scale=6.0)
- break;
- # layout.label(text = '', icon_value=image.preview.icon_id, scale = 10)
- col.label(text=asset.name)
- draw_ratings(col, context, asset=asset)
-
-
-class VIEW3D_PT_blenderkit_ratings(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_ratings"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Please rate"
- bl_context = "objectmode"
-
- @classmethod
- def poll(cls, context):
- #
- p = bpy.context.view_layer.objects.active is not None
- return p
-
- def draw(self, context):
- # TODO make a list of assets inside asset appending code, to happen only when assets are added to the scene.
- # draw asset properties here
- layout = self.layout
- assets = ratings.get_assets_for_rating()
- if len(assets) > 0:
- utils.label_multiline(layout, text='Please help BlenderKit community by rating these assets:')
-
- for a in assets:
- if a.bkit_ratings.rating_work_hours == 0:
- draw_rating_asset(self, context, asset=a)
-
-
-def draw_login_progress(layout):
- layout.label(text='Login through browser')
- layout.label(text='in progress.')
- layout.operator("wm.blenderkit_login_cancel", text="Cancel", icon='CANCEL')
-
-
-class VIEW3D_PT_blenderkit_profile(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_profile"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "BlenderKit Profile"
-
- @classmethod
- def poll(cls, context):
-
- return True
-
- def draw(self, context):
- # draw asset properties here
- layout = self.layout
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
-
- if user_preferences.login_attempt:
- draw_login_progress(layout)
- return
-
- if user_preferences.api_key != '':
- me = bpy.context.window_manager.get('bkit profile')
- if me is not None:
- me = me['user']
- # user name
- if len(me['firstName']) > 0 or len(me['lastName']) > 0:
- layout.label(text=f"Me: {me['firstName']} {me['lastName']}")
- else:
- layout.label(text=f"Me: {me['email']}")
- # layout.label(text='Email: %s' % (me['email']))
-
- # plan information
-
- if me.get('currentPlanName') is not None:
- pn = me['currentPlanName']
- pcoll = icons.icon_collections["main"]
- if pn == 'Free':
- my_icon = pcoll['free']
- else:
- my_icon = pcoll['full']
-
- row = layout.row()
- row.label(text='My plan:')
- row.label(text='%s plan' % pn, icon_value=my_icon.icon_id)
- if pn == 'Free':
- layout.operator("wm.url_open", text="Change plan",
- icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_PLANS
-
- # storage statistics
- # if me.get('sumAssetFilesSize') is not None: # TODO remove this when production server has these too.
- # layout.label(text='My public assets: %i MiB' % (me['sumAssetFilesSize']))
- # if me.get('sumPrivateAssetFilesSize') is not None:
- # layout.label(text='My private assets: %i MiB' % (me['sumPrivateAssetFilesSize']))
- if me.get('remainingPrivateQuota') is not None:
- layout.label(text='My free storage: %i MiB' % (me['remainingPrivateQuota']))
-
- layout.operator("wm.url_open", text="See my uploads",
- icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_USER_ASSETS
-
-
-class MarkNotificationRead(bpy.types.Operator):
- """Mark notification as read here and also on BlenderKit server"""
- bl_idname = "wm.blenderkit_mark_notification_read"
- bl_label = "Mark notification as read"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
- notification_id: bpy.props.IntProperty(
- name="Id",
- description="notification id",
- default=-1)
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- notifications = bpy.context.window_manager['bkit notifications']
- for n in notifications['results']:
- if n['id'] == self.notification_id:
- n['unread'] = 0
- comments_utils.check_notifications_read()
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- api_key = user_preferences.api_key
- comments_utils.mark_notification_read_thread(api_key, self.notification_id)
-
- return {'FINISHED'}
-
-class MarkAllNotificationsRead(bpy.types.Operator):
- """Mark notification as read here and also on BlenderKit server"""
- bl_idname = "wm.blenderkit_mark_notifications_read_all"
- bl_label = "Mark all notifications as read"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- api_key = user_preferences.api_key
- notifications = bpy.context.window_manager['bkit notifications']
- for n in notifications.get('results'):
- if n['unread'] == 1:
- n['unread'] = 0
- comments_utils.mark_notification_read_thread(api_key, n['id'])
-
- comments_utils.check_notifications_read()
- return {'FINISHED'}
-
-class NotificationOpenTarget(bpy.types.Operator):
- """"""
- bl_idname = "wm.blenderkit_open_notification_target"
- bl_label = ""
- bl_description = "Open notification target and mark notification as read"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
- tooltip: bpy.props.StringProperty(default='Open a web page')
- url: bpy.props.StringProperty(default='Runs search and displays the asset bar at the same time')
- notification_id: bpy.props.IntProperty(
- name="Id",
- description="notification id",
- default=-1)
-
- @classmethod
- def description(cls, context, properties):
- return properties.tooltip
-
- def execute(self, context):
- bpy.ops.wm.blenderkit_mark_notification_read(notification_id=self.notification_id)
- bpy.ops.wm.url_open(url=self.url)
- return {'FINISHED'}
-
-
-class LikeComment(bpy.types.Operator):
- """Mark notification as read here and also on BlenderKit server"""
- bl_idname = "wm.blenderkit_like_comment"
- bl_label = "BlenderKit like/dislike comment"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
- asset_id: StringProperty(
- name="Asset Base Id",
- description="Unique id of the asset (hidden)",
- default="",
- options={'SKIP_SAVE'})
-
- comment_id: bpy.props.IntProperty(
- name="Id",
- description="comment id",
- default=-1)
-
- flag: bpy.props.StringProperty(
- name="flag",
- description="Like/dislike comment",
- default="like")
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- api_key = user_preferences.api_key
- comments_utils.send_comment_flag_to_thread(asset_id=self.asset_id, comment_id=self.comment_id, flag=self.flag,
- api_key=api_key)
- return {'FINISHED'}
-
-
-def draw_notification(self, notification, width=600):
- layout = self.layout
- box = layout.box()
- firstline = f"{notification['actor']['string']} {notification['verb']} {notification['target']['string']}"
- box1 = box.box()
- # row = box1.row()
-
- split_last = 0.7
- if notification['description']:
- split_last = 0
-
-
- rows = utils.label_multiline(box1, text=firstline, width=width, split_last = split_last)
-
-
- if notification['description']:
- rows = utils.label_multiline(box, text=notification['description'], width=width, split_last = 0.7)
-
-
- if notification['target']:
- # row = layout.row()
- # split = row.split(factor=.8)
- # split.label(text='')
- # split = split.split()
- # split = rows[-1].split(factor=0.8)
- # split = split.split()
- # split.alignment = 'RIGHT'
- # row = split.row(align = True)
- row = rows[-1]
- row = row.row(align=False)
-
- # row = row.split(factor = 0.7)
-
- op = row.operator('wm.blenderkit_open_notification_target', text='Open page', icon='HIDE_OFF')
- op.tooltip = 'Open the browser on the asset page to comment'
- op.url = paths.get_bkit_url() + notification['target']['url']
- op.notification_id = notification['id']
- # split =
- op = row.operator("wm.blenderkit_mark_notification_read", text="", icon='CANCEL')
- op.notification_id = notification['id']
-
-
-def draw_notifications(self, context, width=600):
- layout = self.layout
- notifications = bpy.context.window_manager.get('bkit notifications')
- if notifications is not None and notifications.get('count')>0:
- row = layout.row()
- # row.alert = True
- split = row.split(factor = 0.7)
- split.label(text='')
- split = split.split()
- split.operator('wm.blenderkit_mark_notifications_read_all', text = 'Mark All Read', icon = 'CANCEL')
- for notification in notifications['results']:
- if notification['unread'] == 1:
- draw_notification(self, notification, width=width)
-
-
-class ShowNotifications(bpy.types.Operator):
- """Show notifications"""
- bl_idname = "wm.show_notifications"
- bl_label = "Show BlenderKit notifications"
- bl_options = {'REGISTER', 'UNDO'}
-
- notification_id: bpy.props.IntProperty(
- name="Id",
- description="notification id",
- default=-1)
-
- @classmethod
- def poll(cls, context):
- return True
-
- def draw(self, context):
- draw_notifications(self, context, width=600)
-
- def execute(self, context):
- wm = bpy.context.window_manager
- return wm.invoke_popup(self, width=600)
-
-
-class VIEW3D_PT_blenderkit_notifications(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_notifications"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "BlenderKit Notifications"
-
- @classmethod
- def poll(cls, context):
- notifications = bpy.context.window_manager.get('bkit notifications')
- if notifications is not None and len(notifications['results']) > 0:
- return True
- return False
-
- def draw(self, context):
- draw_notifications(self, context)
-
-
-class VIEW3D_PT_blenderkit_login(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_login"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "BlenderKit Login"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def draw(self, context):
- layout = self.layout
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
-
- if user_preferences.login_attempt:
- draw_login_progress(layout)
- return
-
- if user_preferences.enable_oauth:
- draw_login_buttons(layout)
-
-
-def draw_panel_model_rating(self, context):
- # o = bpy.context.active_object
- o = utils.get_active_model()
- # print('ratings active',o)
- draw_ratings(self.layout, context, asset=o) # , props)
- # op.asset_type = 'MODEL'
-
-
-def draw_panel_material_upload(self, context):
- o = bpy.context.active_object
- mat = bpy.context.active_object.active_material
-
- props = mat.blenderkit
- layout = self.layout
-
- draw_upload_common(layout, props, 'MATERIAL', context)
-
- # THUMBNAIL
- row = layout.column()
- if props.is_generating_thumbnail:
- row.enabled = False
-
- draw_thumbnail_upload_panel(row, props)
-
- prop_needed(row, props, 'thumbnail', props.has_thumbnail, False)
-
- if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'):
- layout.operator("object.blenderkit_generate_material_thumbnail", text='Render thumbnail with Cycles',
- icon='EXPORT')
- if props.is_generating_thumbnail:
- row = layout.row(align=True)
- row.label(text=props.thumbnail_generating_state, icon='RENDER_STILL')
- op = row.operator('object.kill_bg_process', text="", icon='CANCEL')
- op.process_source = 'MATERIAL'
- op.process_type = 'THUMBNAILER'
- elif props.thumbnail_generating_state != '':
- utils.label_multiline(layout, text=props.thumbnail_generating_state)
-
- layout.prop(props, 'style')
- # if props.style == 'OTHER':
- # layout.prop(props, 'style_other')
- # layout.prop(props, 'engine')
- # if props.engine == 'OTHER':
- # layout.prop(props, 'engine_other')
- # layout.prop(props,'shaders')#TODO autofill on upload
- # row = layout.row()
-
- layout.prop(props, 'pbr')
- layout.prop(props, 'uv')
- layout.prop(props, 'animated')
- layout.prop(props, 'texture_size_meters')
-
- # tname = "." + bpy.context.active_object.active_material.name + "_thumbnail"
- # if props.has_thumbnail and bpy.data.textures.get(tname) is not None:
- # row = layout.row()
- # # row.scale_y = 1.5
- # row.template_preview(bpy.data.textures[tname], preview_id='test')
-
-
-def draw_panel_material_search(self, context):
- wm = context.window_manager
- props = wm.blenderkit_mat
-
- layout = self.layout
- row = layout.row()
- row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
- draw_assetbar_show_hide(row, props)
- utils.label_multiline(layout, text=props.report)
-
- # layout.prop(props, 'search_style')F
- # if props.search_style == 'OTHER':
- # layout.prop(props, 'search_style_other')
- # layout.prop(props, 'search_engine')
- # if props.search_engine == 'OTHER':
- # layout.prop(props, 'search_engine_other')
-
- # draw_panel_categories(self, context)
-
-
-def draw_panel_material_ratings(self, context):
- asset = bpy.context.active_object.active_material
- draw_ratings(self.layout, context, asset) # , props)
- # op.asset_type = 'MATERIAL'
-
-
-def draw_panel_brush_upload(self, context):
- brush = utils.get_active_brush()
- if brush is not None:
- props = brush.blenderkit
-
- layout = self.layout
-
- draw_upload_common(layout, props, 'BRUSH', context)
-
-
-def draw_panel_brush_search(self, context):
- wm = context.window_manager
- props = wm.blenderkit_brush
-
- layout = self.layout
- row = layout.row()
- row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
- draw_assetbar_show_hide(row, props)
- layout.prop(props, "own_only")
-
- utils.label_multiline(layout, text=props.report)
- # draw_panel_categories(self, context)
-
-
-def draw_panel_brush_ratings(self, context):
- # props = utils.get_brush_props(context)
- brush = utils.get_active_brush()
- draw_ratings(self.layout, context, asset=brush) # , props)
- #
- # op.asset_type = 'BRUSH'
-
-
-def draw_login_buttons(layout, invoke=False):
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
-
- if user_preferences.login_attempt:
- draw_login_progress(layout)
- else:
- if invoke:
- layout.operator_context = 'INVOKE_DEFAULT'
- else:
- layout.operator_context = 'EXEC_DEFAULT'
- if user_preferences.api_key == '':
- layout.operator("wm.blenderkit_login", text="Login",
- icon='URL').signup = False
- layout.operator("wm.blenderkit_login", text="Sign up",
- icon='URL').signup = True
-
- else:
- layout.operator("wm.blenderkit_login", text="Login as someone else",
- icon='URL').signup = False
- layout.operator("wm.blenderkit_logout", text="Logout",
- icon='URL')
-
-
-class VIEW3D_PT_blenderkit_advanced_model_search(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_advanced_model_search"
- bl_parent_id = "VIEW3D_PT_blenderkit_unified"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Search filters"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MODEL'
-
- def draw(self, context):
- wm = bpy.context.window_manager
-
- props = wm.blenderkit_models
- layout = self.layout
- layout.separator()
-
- # layout.label(text = "common searches keywords:")
- # layout.prop(props, "search_global_keywords", text = "")
- # layout.prop(props, "search_modifier_keywords")
- # if props.search_engine == 'OTHER':
- # layout.prop(props, "search_engine_keyword")
-
- layout.prop(props, "own_only")
- layout.prop(props, "free_only")
- layout.prop(props, "search_style")
-
- # DESIGN YEAR
- layout.prop(props, "search_design_year", text='Designed in Year')
- if props.search_design_year:
- row = layout.row(align=True)
- row.prop(props, "search_design_year_min", text='Min')
- row.prop(props, "search_design_year_max", text='Max')
-
- # POLYCOUNT
- layout.prop(props, "search_polycount", text='Poly Count ')
- if props.search_polycount:
- row = layout.row(align=True)
- row.prop(props, "search_polycount_min", text='Min')
- row.prop(props, "search_polycount_max", text='Max')
-
- # TEXTURE RESOLUTION
- layout.prop(props, "search_texture_resolution", text='Texture Resolutions')
- if props.search_texture_resolution:
- row = layout.row(align=True)
- row.prop(props, "search_texture_resolution_min", text='Min')
- row.prop(props, "search_texture_resolution_max", text='Max')
-
- # FILE SIZE
- layout.prop(props, "search_file_size", text='File Size (MB)')
- if props.search_file_size:
- row = layout.row(align=True)
- row.prop(props, "search_file_size_min", text='Min')
- row.prop(props, "search_file_size_max", text='Max')
-
- # AGE
- layout.prop(props, "search_condition", text='Condition') # , text ='condition of object new/old e.t.c.')
- layout.prop(props, "quality_limit", slider=True) # , text ='condition of object new/old e.t.c.')
-
- # layout.prop(props, "search_procedural", expand=True)
- # ADULT
- # layout.prop(props, "search_adult") # , text ='condition of object new/old e.t.c.')
-
-
-class VIEW3D_PT_blenderkit_advanced_material_search(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_advanced_material_search"
- bl_parent_id = "VIEW3D_PT_blenderkit_unified"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Search filters"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MATERIAL'
-
- def draw(self, context):
- wm = context.window_manager
- props = wm.blenderkit_mat
- layout = self.layout
- layout.separator()
-
- layout.prop(props, "own_only")
-
- layout.label(text='Texture:')
- col = layout.column()
- col.prop(props, "search_procedural", expand=True)
-
- if props.search_procedural == 'TEXTURE_BASED':
- # TEXTURE RESOLUTION
- layout.prop(props, "search_texture_resolution", text='Texture Resolution')
- if props.search_texture_resolution:
- row = layout.row(align=True)
- row.prop(props, "search_texture_resolution_min", text='Min')
- row.prop(props, "search_texture_resolution_max", text='Max')
-
- # FILE SIZE
- layout.prop(props, "search_file_size", text='File size (MB)')
- if props.search_file_size:
- row = layout.row(align=True)
- row.prop(props, "search_file_size_min", text='Min')
- row.prop(props, "search_file_size_max", text='Max')
- layout.prop(props, "quality_limit", slider=True)
-
-
-class VIEW3D_PT_blenderkit_advanced_HDR_search(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_advanced_HDR_search"
- bl_parent_id = "VIEW3D_PT_blenderkit_unified"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Search filters"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'HDR'
-
- def draw(self, context):
- wm = context.window_manager
- props = wm.blenderkit_HDR
- layout = self.layout
- layout.separator()
-
- layout.prop(props, "own_only")
- layout.prop(props, "true_hdr")
-
-
-class VIEW3D_PT_blenderkit_categories(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_categories"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Categories"
- bl_parent_id = "VIEW3D_PT_blenderkit_unified"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- mode = True
- if ui_props.asset_type == 'BRUSH' and not (context.sculpt_object or context.image_paint_object):
- mode = False
- return ui_props.down_up == 'SEARCH' and mode
-
- def draw(self, context):
- draw_panel_categories(self, context)
-
-
-def draw_scene_import_settings(self, context):
- wm = bpy.context.window_manager
- props = wm.blenderkit_scene
- layout = self.layout
- layout.prop(props, 'switch_after_append')
- # layout.label(text='Import method:')
- row = layout.row()
- row.prop(props, 'append_link', expand=True, icon_only=False)
-
-
-class VIEW3D_PT_blenderkit_import_settings(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_import_settings"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Import settings"
- bl_parent_id = "VIEW3D_PT_blenderkit_unified"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- return ui_props.down_up == 'SEARCH' and ui_props.asset_type in ['MATERIAL', 'MODEL', 'SCENE', 'HDR']
-
- def draw(self, context):
- layout = self.layout
-
- s = context.scene
- wm = bpy.context.window_manager
- ui_props = bpy.context.window_manager.blenderkitUI
-
- if ui_props.asset_type == 'MODEL':
- # noinspection PyCallByClass
- props = wm.blenderkit_models
- layout.prop(props, 'randomize_rotation')
- if props.randomize_rotation:
- layout.prop(props, 'randomize_rotation_amount')
- layout.prop(props, 'perpendicular_snap')
- # if props.perpendicular_snap:
- # layout.prop(props,'perpendicular_snap_threshold')
-
- layout.label(text='Import method:')
- row = layout.row()
- row.prop(props, 'append_method', expand=True, icon_only=False)
-
- if ui_props.asset_type == 'MATERIAL':
- props = wm.blenderkit_mat
- layout.prop(props, 'automap')
- layout.label(text='Import method:')
- row = layout.row()
-
- row.prop(props, 'append_method', expand=True, icon_only=False)
- if ui_props.asset_type == 'SCENE':
- draw_scene_import_settings(self, context)
-
- if ui_props.asset_type == 'HDR':
- props = wm.blenderkit_HDR
-
- if ui_props.asset_type in ['MATERIAL', 'MODEL', 'HDR']:
- layout.prop(props, 'resolution')
- # layout.prop(props, 'unpack_files')
-
-
-class VIEW3D_PT_blenderkit_unified(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_unified"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Find and Upload Assets"
-
- @classmethod
- def poll(cls, context):
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- return user_preferences.panel_behaviour == 'BOTH' or user_preferences.panel_behaviour == 'UNIFIED'
-
- def draw(self, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- wm = bpy.context.window_manager
- layout = self.layout
- # layout.prop_tabs_enum(ui_props, "asset_type", icon_only = True)
-
- row = layout.row()
- # row.scale_x = 1.6
- # row.scale_y = 1.6
- #
- row.prop(ui_props, 'down_up', expand=True, icon_only=False)
- # row.label(text='')
- # row = row.split().row()
- # layout.alert = True
- # layout.alignment = 'CENTER'
- row = layout.row(align=True)
- row.scale_x = 1.6
- row.scale_y = 1.6
- # split = row.split(factor=.
-
- expand_icon = 'TRIA_DOWN'
- if ui_props.asset_type_fold:
- expand_icon = 'TRIA_RIGHT'
- row = layout.row()
- split = row.split(factor=0.15)
- split.prop(ui_props, 'asset_type_fold', icon=expand_icon, icon_only=True, emboss=False)
-
- if ui_props.asset_type_fold:
- pass
- # expanded interface with names in column
- split = split.row()
- split.scale_x = 8
- split.scale_y = 1.6
- # split = row
- # split = layout.row()
- else:
- split = split.column()
-
- split.prop(ui_props, 'asset_type', expand=True, icon_only=ui_props.asset_type_fold)
- # row = layout.column(align = False)
- # layout.prop(ui_props, 'asset_type', expand=False, text='')
-
- w = context.region.width
- if user_preferences.login_attempt:
- draw_login_progress(layout)
- return
-
- if len(user_preferences.api_key) < 20 and user_preferences.asset_counter > 20:
- if user_preferences.enable_oauth:
- draw_login_buttons(layout)
- else:
- op = layout.operator("wm.url_open", text="Get your API Key",
- icon='QUESTION')
- op.url = paths.BLENDERKIT_SIGNUP_URL
- layout.label(text='Paste your API Key:')
- layout.prop(user_preferences, 'api_key', text='')
- layout.separator()
- # if bpy.data.filepath == '':
- # layout.alert = True
- # utils.label_multiline(layout, text="It's better to save your file first.", width=w)
- # layout.alert = False
- # layout.separator()
-
- if ui_props.down_up == 'SEARCH':
- if utils.profile_is_validator():
- search_props = utils.get_search_props()
- layout.prop(search_props, 'search_verification_status')
- layout.prop(search_props, "unrated_only")
-
- if ui_props.asset_type == 'MODEL':
- # noinspection PyCallByClass
- draw_panel_model_search(self, context)
- if ui_props.asset_type == 'SCENE':
- # noinspection PyCallByClass
- draw_panel_scene_search(self, context)
- if ui_props.asset_type == 'HDR':
- # noinspection PyCallByClass
- draw_panel_hdr_search(self, context)
- elif ui_props.asset_type == 'MATERIAL':
- draw_panel_material_search(self, context)
- elif ui_props.asset_type == 'BRUSH':
- if context.sculpt_object or context.image_paint_object:
- # noinspection PyCallByClass
- draw_panel_brush_search(self, context)
- else:
- utils.label_multiline(layout, text='Switch to paint or sculpt mode.', width=context.region.width)
- return
-
-
- elif ui_props.down_up == 'UPLOAD':
- if not ui_props.assetbar_on:
- text = 'Show asset preview - ;'
- else:
- text = 'Hide asset preview - ;'
- op = layout.operator('view3d.blenderkit_asset_bar', text=text, icon='EXPORT')
- op.keep_running = False
- op.do_search = False
- op.tooltip = 'Show/Hide asset preview'
-
- e = s.render.engine
- if e not in ('CYCLES', 'BLENDER_EEVEE'):
- rtext = 'Only Cycles and EEVEE render engines are currently supported. ' \
- 'Please use Cycles for all assets you upload to BlenderKit.'
- utils.label_multiline(layout, rtext, icon='ERROR', width=w)
- return;
-
- if ui_props.asset_type == 'MODEL':
- # utils.label_multiline(layout, "Uploaded models won't be available in b2.79", icon='ERROR')
- if bpy.context.view_layer.objects.active is not None:
- draw_panel_model_upload(self, context)
- else:
- layout.label(text='selet object to upload')
- elif ui_props.asset_type == 'SCENE':
- draw_panel_scene_upload(self, context)
- elif ui_props.asset_type == 'HDR':
- draw_panel_hdr_upload(self, context)
-
- elif ui_props.asset_type == 'MATERIAL':
- # utils.label_multiline(layout, "Uploaded materials won't be available in b2.79", icon='ERROR')
-
- if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
- draw_panel_material_upload(self, context)
- else:
- utils.label_multiline(layout, text='select object with material to upload materials', width=w)
-
- elif ui_props.asset_type == 'BRUSH':
- if context.sculpt_object or context.image_paint_object:
- draw_panel_brush_upload(self, context)
- else:
- layout.label(text='Switch to paint or sculpt mode.')
-
-
-class BlenderKitWelcomeOperator(bpy.types.Operator):
- """Login online on BlenderKit webpage"""
-
- bl_idname = "wm.blenderkit_welcome"
- bl_label = "Welcome to BlenderKit!"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
- step: IntProperty(
- name="step",
- description="Tutorial Step",
- default=0,
- options={'SKIP_SAVE'}
- )
-
- @classmethod
- def poll(cls, context):
- return True
-
- def draw(self, context):
- layout = self.layout
- if self.step == 0:
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
-
- # message = "BlenderKit connects from Blender to an online, " \
- # "community built shared library of models, " \
- # "materials, and brushes. " \
- # "Use addon preferences to set up where files will be saved in the Global directory setting."
- #
- # utils.label_multiline(layout, text=message, width=300)
-
- layout.template_icon(icon_value=self.img.preview.icon_id, scale=18)
-
- # utils.label_multiline(layout, text="\n Let's start by searching for some cool materials?", width=300)
- op = layout.operator("wm.url_open", text='Watch Video Tutorial', icon='QUESTION')
- op.url = paths.BLENDERKIT_MANUAL
-
- else:
- message = "Operator Tutorial called with invalid step"
-
- def execute(self, context):
- if self.step == 0:
- # move mouse:
- # bpy.context.window_manager.windows[0].cursor_warp(1000, 1000)
- # show n-key sidebar (spaces[index] has to be found for view3d too:
- # bpy.context.window_manager.windows[0].screen.areas[5].spaces[0].show_region_ui = False
- ui_props = bpy.context.window_manager.blenderkitUI
- # random_searches = [
- # ('MATERIAL', 'ice'),
- # ('MODEL', 'car'),
- # ('MODEL', 'vase'),
- # ('MODEL', 'grass'),
- # ('MODEL', 'plant'),
- # ('MODEL', 'man'),
- # ('MATERIAL', 'metal'),
- # ('MATERIAL', 'wood'),
- # ('MATERIAL', 'floor'),
- # ('MATERIAL', 'bricks'),
- # ]
- # random_search = random.choice(random_searches)
- # ui_props.asset_type = random_search[0]
- ui_props.asset_type = 'MODEL'
-
- score_limit = 450
- if ui_props.asset_type == 'MATERIAL':
- props = bpy.context.window_manager.blenderkit_mat
-
- elif ui_props.asset_type == 'MODEL':
- props = bpy.context.window_manager.blenderkit_models
- score_limit = 1000
-
- props.search_keywords = '' # random_search[1]
- props.search_keywords += f'+is_free:true+score_gte:{score_limit}+order:-created' # random_search[1]
- # search.search()
- return {'FINISHED'}
-
- def invoke(self, context, event):
- wm = bpy.context.window_manager
- img = utils.get_thumbnail('intro.jpg')
- utils.img_to_preview(img, copy_original=True)
- self.img = img
- w, a, r = utils.get_largest_area(area_type='VIEW_3D')
- if a is not None:
- a.spaces.active.show_region_ui = True
-
- return wm.invoke_props_dialog(self, width=500)
-
-
-def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
- ui_props = context.window_manager.blenderkitUI
-
- author_id = str(asset_data['author'].get('id'))
- wm = bpy.context.window_manager
-
- layout.operator_context = 'INVOKE_DEFAULT'
-
- if from_panel:
- op = layout.operator('wm.blenderkit_menu_rating_upload', text='Add Rating')
- op.asset_name = asset_data['name']
- op.asset_id = asset_data['id']
- op.asset_type = asset_data['assetType']
-
- if from_panel and wm.get('bkit authors') is not None and author_id is not None:
- a = bpy.context.window_manager['bkit authors'].get(author_id)
- if a is not None:
- # utils.p('author:', a)
- op = layout.operator('wm.url_open', text="Open Author's Website")
- if a.get('aboutMeUrl') is not None:
- op.url = a['aboutMeUrl']
- else:
- op.url = paths.get_author_gallery_url(a['id'])
- op = layout.operator('view3d.blenderkit_search', text="Show Assets By Author")
- op.keywords = ''
- op.author_id = author_id
-
- op = layout.operator('view3d.blenderkit_search', text='Search Similar')
- op.esc = True
- op.tooltip = 'Search for similar assets in the library'
- # build search string from description and tags:
- op.keywords = asset_data['name']
- if asset_data.get('description'):
- op.keywords += ' ' + asset_data.get('description') + ' '
- op.keywords += ' '.join(asset_data.get('tags'))
-
- if asset_data.get('canDownload') != 0:
- if len(bpy.context.selected_objects) > 0 and ui_props.asset_type == 'MODEL':
- aob = bpy.context.active_object
- if aob is None:
- aob = bpy.context.selected_objects[0]
- op = layout.operator('scene.blenderkit_download', text='Replace Active Models')
- op.tooltip = "Replace all selected models with this one."
-
- # this checks if the menu got called from right-click in assetbar(then index is 0 - x) or
- # from a panel(then replacement happens from the active model)
- if from_panel:
- # called from addon panel
- op.asset_base_id = asset_data['assetBaseId']
- else:
- op.asset_index = ui_props.active_index
-
- # op.asset_type = ui_props.asset_type
- op.model_location = aob.location
- op.model_rotation = aob.rotation_euler
- op.target_object = aob.name
- op.material_target_slot = aob.active_material_index
- op.replace = True
- op.replace_resolution = False
-
- # resolution replacement operator
- # if asset_data['downloaded'] == 100: # only show for downloaded/used assets
- # if ui_props.asset_type in ('MODEL', 'MATERIAL'):
- # layout.menu(OBJECT_MT_blenderkit_resolution_menu.bl_idname)
-
- if ui_props.asset_type in ('MODEL', 'MATERIAL', 'HDR') and \
- utils.get_param(asset_data, 'textureResolutionMax') is not None and \
- utils.get_param(asset_data, 'textureResolutionMax') > 512:
-
- s = bpy.context.scene
-
- col = layout.column()
- col.operator_context = 'INVOKE_DEFAULT'
-
- if from_panel:
- # Called from addon panel
-
- if asset_data.get('resolution'):
- op = col.operator('scene.blenderkit_download', text='Replace asset resolution')
- op.asset_base_id = asset_data['assetBaseId']
- if asset_data['assetType'] == 'MODEL':
- o = utils.get_active_model()
- op.model_location = o.location
- op.model_rotation = o.rotation_euler
- op.target_object = o.name
- op.material_target_slot = o.active_material_index
-
- elif asset_data['assetType'] == 'MATERIAL':
- aob = bpy.context.active_object
- op.model_location = aob.location
- op.model_rotation = aob.rotation_euler
- op.target_object = aob.name
- op.material_target_slot = aob.active_material_index
- op.replace_resolution = True
- op.replace = False
-
- op.invoke_resolution = True
- op.max_resolution = asset_data.get('max_resolution',
- 0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
-
- elif asset_data['assetBaseId'] in s['assets used'].keys() and asset_data['assetType'] != 'hdr':
- # HDRs are excluded from replacement, since they are always replaced.
- # called from asset bar:
- op = col.operator('scene.blenderkit_download', text='Replace asset resolution')
-
- op.asset_index = ui_props.active_index
- # op.asset_type = ui_props.asset_type
- op.replace_resolution = True
- op.replace = False
- op.invoke_resolution = True
- o = utils.get_active_model()
- if o and o.get('asset_data'):
- if o['asset_data']['assetBaseId'] == bpy.context.window_manager['search results'][
- ui_props.active_index]:
- op.model_location = o.location
- op.model_rotation = o.rotation_euler
- else:
- op.model_location = (0, 0, 0)
- op.model_rotation = (0, 0, 0)
- op.max_resolution = asset_data.get('max_resolution',
- 0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
- # print('operator res ', resolution)
- # op.resolution = resolution
-
- wm = bpy.context.window_manager
- profile = wm.get('bkit profile')
- if profile is not None:
- # validation
-
- if author_id == str(profile['user']['id']) or utils.profile_is_validator():
- layout.label(text='Management tools:')
-
- row = layout.row()
- row.operator_context = 'INVOKE_DEFAULT'
- op = layout.operator('wm.blenderkit_fast_metadata', text='Edit Metadata', icon='GREASEPENCIL')
- op.asset_id = asset_data['id']
- op.asset_type = asset_data['assetType']
-
- if author_id == str(profile['user']['id']):
- row.operator_context = 'EXEC_DEFAULT'
- op = layout.operator('wm.blenderkit_url', text='Edit Metadata (browser)', icon='GREASEPENCIL')
- op.url = paths.get_bkit_url() + paths.BLENDERKIT_USER_ASSETS + f"/{asset_data['assetBaseId']}/?edit#"
-
- row.operator_context = 'INVOKE_DEFAULT'
-
- if asset_data['assetType'] == 'model':
- op = layout.operator('object.blenderkit_regenerate_thumbnail', text='Regenerate thumbnail')
- op.asset_index = ui_props.active_index
- elif asset_data['assetType'] == 'material':
- op = layout.operator('object.blenderkit_regenerate_material_thumbnail', text='Regenerate thumbnail')
- op.asset_index = ui_props.active_index
- # op.asset_id = asset_data['id']
- # op.asset_type = asset_data['assetType']
-
- if author_id == str(profile['user']['id']):
- row = layout.row()
- row.operator_context = 'INVOKE_DEFAULT'
- op = row.operator('object.blenderkit_change_status', text='Delete')
- op.asset_id = asset_data['id']
- op.state = 'deleted'
-
- if utils.profile_is_validator():
- layout.label(text='Dev Tools:')
-
- op = layout.operator('object.blenderkit_print_asset_debug', text='Print asset debug')
- op.asset_id = asset_data['id']
-
-
-# def draw_asset_resolution_replace(self, context, resolution):
-# layout = self.layout
-# ui_props = bpy.context.window_manager.blenderkitUI
-#
-# op = layout.operator('scene.blenderkit_download', text=resolution)
-# if ui_props.active_index == -3:
-# # This happens if the command is called from addon panel
-# o = utils.get_active_model()
-# op.asset_base_id = o['asset_data']['assetBaseId']
-#
-# else:
-# op.asset_index = ui_props.active_index
-#
-# op.asset_type = ui_props.asset_type
-# if len(bpy.context.selected_objects) > 0: # and ui_props.asset_type == 'MODEL':
-# aob = bpy.context.active_object
-# op.model_location = aob.location
-# op.model_rotation = aob.rotation_euler
-# op.target_object = aob.name
-# op.material_target_slot = aob.active_material_index
-# op.replace_resolution = True
-# print('operator res ', resolution)
-# op.resolution = resolution
-
-
-# class OBJECT_MT_blenderkit_resolution_menu(bpy.types.Menu):
-# bl_label = "Replace Asset Resolution"
-# bl_idname = "OBJECT_MT_blenderkit_resolution_menu"
-#
-# def draw(self, context):
-# ui_props = context.window_manager.blenderkitUI
-#
-# # sr = bpy.context.window_manager['search results']
-#
-# # sr = bpy.context.window_manager['search results']
-# # asset_data = sr[ui_props.active_index]
-#
-# for k in resolutions.resolution_props_to_server.keys():
-# draw_asset_resolution_replace(self, context, k)
-
-
-class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
- bl_label = "Asset options:"
- bl_idname = "OBJECT_MT_blenderkit_asset_menu"
-
- def draw(self, context):
- ui_props = context.window_manager.blenderkitUI
-
- sr = bpy.context.window_manager['search results']
- asset_data = sr[ui_props.active_index]
- draw_asset_context_menu(self.layout, context, asset_data, from_panel=False)
-
-
-def numeric_to_str(s):
- if s:
- if s < 1:
- s = str(round(s, 1))
- else:
- s = str(round(s))
- else:
- s = '-'
- return s
-
-
-def push_op_left(layout, strength=3):
- for a in range(0, strength):
- layout.label(text='')
-
-
-def label_or_url_or_operator(layout, text='', tooltip='', url='', operator=None, operator_kwargs={}, icon_value=None,
- icon=None):
- '''automatically switch between different layout options for linking or tooltips'''
- layout.emboss = 'NONE'
-
- if operator is not None:
- if icon:
- op = layout.operator(operator, text=text, icon=icon)
- elif icon_value:
- op = layout.operator(operator, text=text, icon_value=icon_value)
- else:
- op = layout.operator(operator, text=text)
- for kwarg in operator_kwargs.keys():
- if type(operator_kwargs[kwarg]) == str:
- quoatation = "'"
- else:
- quoatation = ""
- exec(f"op.{kwarg} = {quoatation}{operator_kwargs[kwarg]}{quoatation}")
- push_op_left(layout, strength=2)
-
- return
- if url != '':
- if icon:
- op = layout.operator('wm.blenderkit_url', text=text, icon=icon)
- elif icon_value:
- op = layout.operator('wm.blenderkit_url', text=text, icon_value=icon_value)
- else:
- op = layout.operator('wm.blenderkit_url', text=text)
- op.url = url
- op.tooltip = tooltip
- push_op_left(layout, strength=5)
-
- return
- if tooltip != '':
- if icon:
- op = layout.operator('wm.blenderkit_tooltip', text=text, icon=icon)
- elif icon_value:
- op = layout.operator('wm.blenderkit_tooltip', text=text, icon_value=icon_value)
- else:
- op = layout.operator('wm.blenderkit_tooltip', text=text)
- op.tooltip = tooltip
-
- # these are here to move the text to left, since operators can only center text by default
- push_op_left(layout, strength=3)
- return
- if icon:
- layout.label(text=text, icon=icon)
- elif icon_value:
- layout.label(text=text, icon_value=icon_value)
- else:
- layout.label(text=text)
-
-
-class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties):
- """Generate Cycles thumbnail for model assets"""
- bl_idname = "wm.blenderkit_asset_popup"
- bl_label = "BlenderKit asset popup"
-
- width = 800
-
- @classmethod
- def poll(cls, context):
- return True
-
- def draw_menu(self, context, layout):
- # layout = layout.column()
- draw_asset_context_menu(layout, context, self.asset_data, from_panel=False)
-
- def draw_property(self, layout, left, right, icon=None, icon_value=None, url='', tooltip='', operator=None,
- operator_kwargs={}):
- right = str(right)
- row = layout.row()
- split = row.split(factor=0.35)
- split.alignment = 'RIGHT'
- split.label(text=left)
- split = split.split()
- split.alignment = 'LEFT'
- # split for questionmark:
- if url != '':
- split = split.split(factor=0.6)
- label_or_url_or_operator(split, text=right, tooltip=tooltip, url=url, operator=operator,
- operator_kwargs=operator_kwargs, icon_value=icon_value, icon=icon)
- # additional questionmark icon where it's important?
- if url != '':
- split = split.split()
- op = split.operator('wm.blenderkit_url', text='', icon='QUESTION')
- op.url = url
- op.tooltip = tooltip
-
- def draw_asset_parameter(self, layout, key='', pretext='', do_search=False, decimal = True):
- parameter = utils.get_param(self.asset_data, key)
- if parameter == None:
- return
- if type(parameter) == int:
- if decimal:
- parameter = f"{parameter:,d}"
- else:
- parameter = f"{parameter}"
- elif type(parameter) == float:
- parameter = f"{parameter:,.1f}"
- if do_search:
- kwargs = {
- 'esc': True,
- 'keywords': f'+{key}:{parameter}',
- 'tooltip': f'search by {parameter}',
- }
- self.draw_property(layout, pretext, parameter, operator='view3d.blenderkit_search', operator_kwargs=kwargs)
- else:
- self.draw_property(layout, pretext, parameter)
-
- def draw_description(self, layout, width=250):
- if len(self.asset_data['description']) > 0:
- box = layout.box()
- box.scale_y = 0.4
- box.label(text='Description')
- box.separator()
- link_more = utils.label_multiline(box, self.asset_data['description'], width=width, max_lines=10)
- if link_more:
- row = box.row()
- row.scale_y = 2
- op = row.operator('wm.blenderkit_url', text='See full description', icon='URL')
- op.url = paths.get_asset_gallery_url(self.asset_data['assetBaseId'])
- op.tooltip = 'Read full description on website'
- box.separator()
-
- def draw_properties(self, layout, width=250):
-
- # if type(self.asset_data['parameters']) == list:
- # mparams = utils.params_to_dict(self.asset_data['parameters'])
- # else:
- # mparams = self.asset_data['parameters']
- mparams = self.asset_data['dictParameters']
-
- pcoll = icons.icon_collections["main"]
-
- box = layout.box()
-
- box.scale_y = 0.4
- box.label(text='Properties')
- box.separator()
-
- if self.asset_data.get('license') == 'cc_zero':
- t = 'CC Zero '
- icon = pcoll['cc0']
-
- else:
- t = 'Royalty free'
- icon = pcoll['royalty_free']
-
- self.draw_property(box,
- 'License', t,
- # icon_value=icon.icon_id,
- url="https://www.blenderkit.com/docs/licenses/",
- tooltip='All BlenderKit assets are available for commercial use. \n' \
- 'Click to read more about BlenderKit licenses on the website'
- )
-
- if upload.can_edit_asset(asset_data=self.asset_data):
- icon = pcoll[self.asset_data['verificationStatus']]
- verification_status_tooltips = {
- 'uploading': "Your asset got stuck during upload. Probably, your file was too large "
- "or your connection too slow or interrupting. If you have repeated issues, "
- "please contact us and let us know, it might be a bug",
- 'uploaded': "Your asset uploaded successfully. Yay! If it's public, "
- "it's awaiting validation. If it's private, use it",
- 'on_hold': "Your asset needs some (usually smaller) fixes, "
- "so we can make it public for everybody."
- " Please check your email to see the feedback "
- "that we send to every creator personally",
- 'rejected': "The asset has serious quality issues, " \
- "and it's probable that it might be good to start " \
- "all over again or try with something simpler. " \
- "You also get personal feedback into your e-mail, " \
- "since we believe that together, we can all learn " \
- "to become awesome 3D artists",
- 'deleted': "You deleted this asset",
- 'validated': "Your asset passed our validation process, "
- "and is now available to BlenderKit users"
-
- }
- self.draw_property(box,
- 'Verification',
- self.asset_data['verificationStatus'],
- icon_value=icon.icon_id,
- url="https://www.blenderkit.com/docs/validation-status/",
- tooltip=verification_status_tooltips[self.asset_data['verificationStatus']]
-
- )
- # resolution/s
- resolution = utils.get_param(self.asset_data, 'textureResolutionMax')
-
- if resolution is not None:
- fs = self.asset_data['files']
-
- ress = f"{int(round(resolution / 1024, 0))}K"
- self.draw_property(box, 'Resolution', ress,
- tooltip='Maximal resolution of textures in this asset.\n' \
- 'Most texture asset have also lower resolutions generated.\n' \
- 'Go to BlenderKit add-on import settings to set default resolution')
-
- if fs and len(fs) > 2 and utils.profile_is_validator():
- resolutions = ''
- list.sort(fs, key=lambda f: f['fileType'])
- for f in fs:
- if f['fileType'].find('resolution') > -1:
- resolutions += f['fileType'][11:] + ' '
- resolutions = resolutions.replace('_', '.')
- self.draw_property(box, 'Generated', resolutions)
-
- self.draw_asset_parameter(box, key='designer', pretext='Designer', do_search=True)
- self.draw_asset_parameter(box, key='manufacturer', pretext='Manufacturer',
- do_search=True)
- self.draw_asset_parameter(box, key='designCollection', pretext='Collection', do_search=True)
- self.draw_asset_parameter(box, key='designVariant', pretext='Variant')
- self.draw_asset_parameter(box, key='designYear', pretext='Design year', decimal = False)
-
- self.draw_asset_parameter(box, key='faceCount', pretext='Face count')
- # self.draw_asset_parameter(box, key='thumbnailScale', pretext='Preview scale')
- # self.draw_asset_parameter(box, key='purePbr', pretext='Pure PBR')
- # self.draw_asset_parameter(box, key='productionLevel', pretext='Readiness')
- # self.draw_asset_parameter(box, key='condition', pretext='Condition')
- if utils.profile_is_validator():
- self.draw_asset_parameter(box, key='materialStyle', pretext='Style')
- self.draw_asset_parameter(box, key='modelStyle', pretext='Style')
-
- if utils.get_param(self.asset_data, 'dimensionX'):
- t = utils.fmt_dimensions(mparams)
- self.draw_property(box, 'Size', t)
- if self.asset_data.get('filesSize'):
- fs = self.asset_data['filesSize']
- fsmb = fs // (1024 * 1024)
- fskb = fs % 1024
- if fsmb == 0:
- self.draw_property(box, 'Original size', f'{fskb} KB')
- else:
- self.draw_property(box, 'Original size', f'{fsmb} MB')
- # Tags section
- # row = box.row()
- # letters_on_row = 0
- # max_on_row = width / 10
- # for tag in self.asset_data['tags']:
- # if tag in ('manifold', 'uv', 'non-manifold'):
- # # these are sometimes accidentally stored in the lib
- # continue
- #
- # # row.emboss='NONE'
- # # we need to split wisely
- # remaining_row = (max_on_row - letters_on_row) / max_on_row
- # split_factor = (len(tag) / max_on_row) / remaining_row
- # row = row.split(factor=split_factor)
- # letters_on_row += len(tag)
- # if letters_on_row > max_on_row:
- # letters_on_row = len(tag)
- # row = box.row()
- # remaining_row = (max_on_row - letters_on_row) / max_on_row
- # split_factor = (len(tag) / max_on_row) / remaining_row
- # row = row.split(factor=split_factor)
- #
- # op = row.operator('wm')
- # op = row.operator('view3d.blenderkit_search', text=tag)
- # op.tooltip = f'Search items with tag {tag}'
- # # build search string from description and tags:
- # op.keywords = f'+tags:{tag}'
-
- # self.draw_property(box, 'Tags', self.asset_data['tags']) #TODO make them clickable!
-
- # Free/Full plan or private Access
- plans_tooltip = 'BlenderKit has 2 plans:\n' \
- ' * Free plan - more than 50% of all assets\n' \
- ' * Full plan - unlimited access to everything\n' \
- 'Click to go to subscriptions page'
- plans_link = 'https://www.blenderkit.com/plans/pricing/'
- if self.asset_data['isPrivate']:
- t = 'Private'
- self.draw_property(box, 'Access', t, icon='LOCKED')
- elif self.asset_data['isFree']:
- t = 'Free plan'
- icon = pcoll['free']
- self.draw_property(box, 'Access', t,
- icon_value=icon.icon_id,
- tooltip=plans_tooltip,
- url=plans_link)
- else:
- t = 'Full plan'
- icon = pcoll['full']
- self.draw_property(box, 'Access', t,
- icon_value=icon.icon_id,
- tooltip=plans_tooltip,
- url=plans_link)
- if utils.profile_is_validator():
- date = self.asset_data['created'][:10]
- date = f"{date[8:10]}. {date[5:7]}. {date[:4]}"
- self.draw_property(box, 'Created', date)
- if utils.asset_from_newer_blender_version(self.asset_data):
- # row = box.row()
- box.alert = True
- self.draw_property(box,
- 'Blender version',
- self.asset_data['sourceAppVersion'],
- # icon='ERROR',
- tooltip='Asset is from a newer Blender version and might work incorrectly in your scene',
- )
- box.alert = False
- box.separator()
-
- def draw_author_area(self, context, layout, width=330):
- self.draw_author(context, layout, width=width)
-
- def draw_author(self, context, layout, width=330):
- image_split = 0.25
- text_width = width
- authors = bpy.context.window_manager['bkit authors']
- a = authors.get(self.asset_data['author']['id'])
- if a is not None: # or a is '' or (a.get('gravatarHash') is not None and a.get('gravatarImg') is None):
-
- row = layout.row()
- author_box = row.box()
- author_box.scale_y = 0.6 # get text lines closer to each other
- author_box.label(text='Author') # just one extra line to give spacing
- if hasattr(self, 'gimg'):
-
- author_left = author_box.split(factor=image_split)
- author_left.template_icon(icon_value=self.gimg.preview.icon_id, scale=7)
- text_area = author_left.split()
- text_width = int(text_width * (1 - image_split))
- else:
- text_area = author_box
-
- author_right = text_area.column()
- row = author_right.row()
- col = row.column()
-
- utils.label_multiline(col, text=a['tooltip'], width=text_width)
- # check if author didn't fill any data about himself and prompt him if that's the case
- if utils.user_is_owner(asset_data=self.asset_data) and a.get('aboutMe') is not None and len(
- a.get('aboutMe', '')) == 0:
- row = col.row()
- row.enabled = False
- row.label(text='Please introduce yourself to the community!')
-
- op = col.operator('wm.blenderkit_url', text='Edit your profile')
- op.url = 'https://www.blenderkit.com/profile'
- op.tooltip = 'Edit your profile on BlenderKit webpage'
-
- button_row = author_box.row()
- button_row.scale_y = 2.0
-
- if a.get('aboutMeUrl') is not None:
- url = a['aboutMeUrl']
- text = url
- if len(url) > 45:
- text = url[:45] + '...'
- op = button_row.operator('wm.url_open', text=text)
- op.url = url
- button_row = author_box.row()
- button_row.scale_y = 2.0
-
- url = paths.get_author_gallery_url(a['id'])
- text = "Author's Profile"
-
- op = button_row.operator('wm.url_open', text=text)
- op.url = url
-
- op = button_row.operator('view3d.blenderkit_search', text="Find Assets By Author")
- op.esc = True
- op.keywords = ''
- op.author_id = self.asset_data['author']['id']
-
- def draw_thumbnail_box(self, layout, width=250):
- layout.emboss = 'NORMAL'
-
- box_thumbnail = layout.box()
-
- box_thumbnail.scale_y = .4
- box_thumbnail.template_icon(icon_value=self.img.preview.icon_id, scale=width * .12)
-
- # op = row.operator('view3d.asset_drag_drop', text='Drag & Drop from here', depress=True)
- # From here on, only ratings are drawn, which won't be displayed for private assets from now on.
-
- if not self.asset_data['isPrivate']:
- row = box_thumbnail.row()
- row.alignment = 'EXPAND'
-
- # display_ratings = can_display_ratings(self.asset_data)
- rc = self.asset_data.get('ratingsCount')
- show_rating_threshold = 0
- show_rating_prompt_threshold = 5
-
- if rc:
- rcount = min(rc['quality'], rc['workingHours'])
- else:
- rcount = 0
- if rcount >= show_rating_threshold or upload.can_edit_asset(asset_data=self.asset_data):
- s = numeric_to_str(self.asset_data['score'])
- q = numeric_to_str(self.asset_data['ratingsAverage'].get('quality'))
- c = numeric_to_str(self.asset_data['ratingsAverage'].get('workingHours'))
- else:
- s = '-'
- q = '-'
- c = '-'
-
- pcoll = icons.icon_collections["main"]
-
- row.emboss = 'NONE'
- op = row.operator('wm.blenderkit_tooltip', text=str(s), icon_value=pcoll['trophy'].icon_id)
- op.tooltip = 'Asset score calculated from user ratings. \n\n' \
- 'Score = average quality × median complexity × 10*\n\n *Happiness multiplier'
- row.label(text=' ')
-
- tooltip_extension = f'.\n\nRatings results are shown for assets with more than {show_rating_threshold} ratings'
- op = row.operator('wm.blenderkit_tooltip', text=str(q), icon='SOLO_ON')
- op.tooltip = f"Quality, average from {rc['quality']} rating{'' if rc['quality'] == 1 else 's'}" \
- f"{tooltip_extension if rcount <= show_rating_threshold else ''}"
- row.label(text=' ')
-
- op = row.operator('wm.blenderkit_tooltip', text=str(c), icon_value=pcoll['dumbbell'].icon_id)
- op.tooltip = f"Complexity, median from {rc['workingHours']} rating{'' if rc['workingHours'] == 1 else 's'}" \
- f"{tooltip_extension if rcount <= show_rating_threshold else ''}"
-
- if rcount <= show_rating_prompt_threshold:
- box_thumbnail.alert = True
- box_thumbnail.label(text=f"")
- box_thumbnail.label(
- text=f"This asset has only {rcount} rating{'' if rcount == 1 else 's'}, please rate.")
- # box_thumbnail.label(text=f"Please rate this asset.")
-
- row = box_thumbnail.row()
- row.alert = False
-
- row.scale_y = 3
- ui_props = bpy.context.window_manager.blenderkitUI
- if self.asset_data.get('canDownload', True):
- row.prop(ui_props, 'drag_init_button', icon='MOUSE_LMB_DRAG', text='Click / Drag from here', emboss=True)
- else:
- op = layout.operator('wm.blenderkit_url', text='Unlock this asset', icon='UNLOCKED')
- op.url = paths.get_bkit_url() + '/get-blenderkit/' + self.asset_data['id'] + '/?from_addon=True'
-
- def draw_menu_desc_author(self, context, layout, width=330):
- box = layout.column()
-
- box.emboss = 'NORMAL'
- # left - tooltip & params
- row = box.row()
- split_factor = 0.7
- split_left = row.split(factor=split_factor)
- col = split_left.column()
- width_left = int(width * split_factor)
- self.draw_description(col, width=width_left)
-
- self.draw_properties(col, width=width_left)
-
- # right - menu
- split_right = split_left.split()
- col = split_right.column()
- self.draw_menu(context, col)
-
- # author
- self.draw_author_area(context, box, width=width)
-
- # self.draw_author_area(context, box, width=width)
- #
- # col = box.column_flow(columns=2)
- # self.draw_menu(context, col)
- #
- #
- # # self.draw_description(box, width=int(width))
- # self.draw_properties(box, width=int(width))
-
- # define enum flags
-
- def draw_titlebar(self, context, layout):
- top_drag_bar = layout.box()
- bcats = bpy.context.window_manager['bkit_categories']
-
- cat_path = categories.get_category_path(bcats,
- self.asset_data['category'])[1:]
-
- cat_path_names = categories.get_category_name_path(bcats,
- self.asset_data['category'])[1:]
-
- aname = self.asset_data['displayName']
- aname = aname[0].upper() + aname[1:]
-
- if 1:
- name_row = top_drag_bar.row()
- # name_row = name_row.split(factor=0.5)
- # name_row = name_row.column()
- # name_row = name_row.row()
- for i, c in enumerate(cat_path):
- cat_name = cat_path_names[i]
- op = name_row.operator('view3d.blenderkit_asset_bar_widget', text=cat_name + ' >', emboss=True)
- op.do_search = True
- op.keep_running = True
- op.tooltip = f"Browse {cat_name} category"
- op.category = c
- # name_row.label(text='>')
-
- name_row.label(text=aname)
- push_op_left(name_row, strength=3)
- op = name_row.operator('view3d.close_popup_button', text='', icon='CANCEL')
-
- def draw_comment(self, context, layout, comment, width=330):
- row = layout.row()
- # print(comment)
- if comment['level'] > 0:
- split = row.split(factor=0.05 * comment['level'])
- split.label(text='')
- row = split.split()
- box = row.box()
- box.emboss = 'NORMAL'
- row = box.row()
- split = row.split(factor=0.8)
- is_moderator = comment['userModerator']
- if is_moderator:
- role_text = f" - moderator"
- else:
- role_text = ""
- row = split.row()
- row.enabled = False
- row.label(text=f"{comment['submitDate']} - {comment['userName']}{role_text}")
- removal = False
- likes = 0
- dislikes = 0
- for l in comment['flags']:
- if l['flag'] == 'like':
- likes += 1
- if l['flag'] == 'dislike':
- dislikes += 1
- if l['flag'] == 'removal':
- removal = True
- # row = box.row()
- split1 = split.split()
- # split1.emboss = 'NONE'
- op = split1.operator('wm.blenderkit_like_comment', text=str(likes), icon='TRIA_UP')
- op.asset_id = self.asset_data['assetBaseId']
- op.comment_id = comment['id']
- op.flag = 'like'
- op = split1.operator('wm.blenderkit_like_comment', text=str(dislikes), icon='TRIA_DOWN')
- op.asset_id = self.asset_data['assetBaseId']
- op.comment_id = comment['id']
- op.flag = 'dislike'
- # op = split1.operator('wm.blenderkit_like_comment', text='report', icon='ERROR')
- # op.asset_id = self.asset_data['assetBaseId']
- # op.comment_id = comment['id']
- # op.flag = 'removal'
- if removal:
- row.alert = True
- row.label(text='', icon='ERROR')
-
- rows = utils.label_multiline(box, text=comment['comment'], width=width * (1 - 0.05 * comment['level']))
-
- row = rows[-1]
- split = row.split(factor=.8)
- split.label(text='')
- split = split.split()
- op = split.operator('wm.blenderkit_url', text='Reply', icon='GREASEPENCIL')
- op.tooltip = 'Open the browser on the asset page to comment'
- op.url = paths.get_bkit_url() + f"/asset-gallery-detail/{self.asset_data['id']}/"
- # box.label(text=str(comment['flags']))
-
- def draw(self, context):
- layout = self.layout
- # top draggable bar with name of the asset
- top_row = layout.row()
- self.draw_titlebar(context, top_row)
- # left side
- row = layout.row(align=True)
- split_ratio = 0.45
- split_left = row.split(factor=split_ratio)
- left_column = split_left.column()
- self.draw_thumbnail_box(left_column, width=int(self.width * split_ratio))
- # self.draw_description(left_column, width = int(self.width*split_ratio))
- # right split
- split_right = split_left.split()
- self.draw_menu_desc_author(context, split_right, width=int(self.width * (1 - split_ratio)))
-
- if not utils.user_is_owner(asset_data=self.asset_data):
- # Draw ratings, but not for owners of assets - doesn't make sense.
- ratings_box = layout.box()
- ratings.draw_ratings_menu(self, context, ratings_box)
- # else:
- # ratings_box.label('Here you should find ratings, but you can not rate your own assets ;)')
-
- tip_box = layout.box()
- tip_box.label(text=self.tip)
- # comments
- if utils.profile_is_validator():
- comments = bpy.context.window_manager.get('asset comments', {})
- self.comments = comments.get(self.asset_data['assetBaseId'], [])
- if self.comments is not None:
- for comment in self.comments:
- self.draw_comment(context, layout, comment, width=self.width)
-
- def prefill_ratings(self):
- # pre-fill ratings
- ratings = ratings_utils.get_rating_local(self.asset_id)
- if ratings and ratings.get('quality'):
- self.rating_quality = ratings['quality']
- if ratings and ratings.get('working_hours'):
- wh = int(ratings['working_hours'])
- whs = str(wh)
- if wh in self.possible_wh_values:
- self.rating_work_hours_ui = whs
- if wh < 6 and wh in self.possible_wh_values_1_5:
- self.rating_work_hours_ui_1_5 = whs
- if wh < 11 and wh in self.possible_wh_values_1_10:
- self.rating_work_hours_ui_1_10 = whs
-
- def execute(self, context):
- wm = context.window_manager
- ui_props = context.window_manager.blenderkitUI
- ui_props.draw_tooltip = False
- sr = bpy.context.window_manager['search results']
- asset_data = sr[ui_props.active_index]
- self.asset_data = asset_data
-
- self.img = ui.get_large_thumbnail_image(asset_data)
- utils.img_to_preview(self.img, copy_original=True)
-
- self.asset_type = asset_data['assetType']
- self.asset_id = asset_data['id']
- # self.tex = utils.get_hidden_texture(self.img)
- # self.tex.update_tag()
-
- authors = bpy.context.window_manager['bkit authors']
- a = authors.get(asset_data['author']['id'])
-
- if a is not None and a.get('gravatarImg') is not None:
- self.gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash'])
-
- bl_label = asset_data['name']
- self.tip = search.get_random_tip()
- self.tip = self.tip.replace('\n', '')
-
- # pre-fill ratings
- self.prefill_ratings()
-
- # get comments
- if utils.profile_is_validator():
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- api_key = user_preferences.api_key
- comments = comments_utils.get_comments_local(asset_data['assetBaseId'])
- # if comments is None:
- comments_utils.get_comments_thread(asset_data['assetBaseId'], api_key)
- comments = bpy.context.window_manager.get('asset comments', {})
- self.comments = comments.get(asset_data['assetBaseId'], [])
-
- return wm.invoke_popup(self, width=self.width)
-
-
-class OBJECT_MT_blenderkit_login_menu(bpy.types.Menu):
- bl_label = "BlenderKit login/signup:"
- bl_idname = "OBJECT_MT_blenderkit_login_menu"
-
- def draw(self, context):
- layout = self.layout
-
- # utils.label_multiline(layout, text=message)
- draw_login_buttons(layout)
-
-
-class SetCategoryOperator(bpy.types.Operator):
- """Visit subcategory"""
- bl_idname = "view3d.blenderkit_set_category"
- bl_label = "BlenderKit Set Active Category"
- bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
-
- category: bpy.props.StringProperty(
- name="Category",
- description="set this category active",
- default="")
-
- asset_type: bpy.props.StringProperty(
- name="Asset Type",
- description="asset type",
- default="")
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- acat = bpy.context.window_manager['active_category'][self.asset_type]
- if self.category == '':
- acat.remove(acat[-1])
- else:
- acat.append(self.category)
- # we have to write back to wm. Thought this should happen with original list.
- bpy.context.window_manager['active_category'][self.asset_type] = acat
- return {'FINISHED'}
-
-
-class ClosePopupButton(bpy.types.Operator):
- """Close popup window"""
- bl_idname = "view3d.close_popup_button"
- bl_label = "Close popup"
- bl_options = {'REGISTER', 'INTERNAL'}
-
- @classmethod
- def poll(cls, context):
- return True
-
- def win_close(self):
- VK_ESCAPE = 0x1B
- ctypes.windll.user32.keybd_event(VK_ESCAPE)
- return True
-
- def mouse_trick(self, context, x, y):
- # import time
- context.area.tag_redraw()
- w = context.window
- w.cursor_warp(w.x + 15, w.y + w.height - 15);
- # time.sleep(.12)
- w.cursor_warp(x, y);
- context.area.tag_redraw()
-
- def invoke(self, context, event):
- if platform.system() == 'Windows':
- self.win_close()
- else:
- self.mouse_trick(context, event.mouse_x, event.mouse_y)
- return {'FINISHED'}
-
-
-class UrlPopupDialog(bpy.types.Operator):
- """Generate Cycles thumbnail for model assets"""
- bl_idname = "wm.blenderkit_url_dialog"
- bl_label = "BlenderKit message:"
- bl_options = {'REGISTER', 'INTERNAL'}
-
- url: bpy.props.StringProperty(
- name="Url",
- description="url",
- default="")
-
- link_text: bpy.props.StringProperty(
- name="Url",
- description="url",
- default="Go to website")
-
- message: bpy.props.StringProperty(
- name="Text",
- description="text",
- default="")
-
- # @classmethod
- # def poll(cls, context):
- # return bpy.context.view_layer.objects.active is not None
-
- def draw(self, context):
- layout = self.layout
- utils.label_multiline(layout, text=self.message, width=300)
-
- layout.active_default = True
- op = layout.operator("wm.url_open", text=self.link_text, icon='QUESTION')
- if not utils.user_logged_in():
- utils.label_multiline(layout,
- text='Already subscribed? You need to login to access your Full Plan.',
- width=300)
-
- layout.operator_context = 'EXEC_DEFAULT'
- layout.operator("wm.blenderkit_login", text="Login",
- icon='URL').signup = False
- op.url = self.url
-
- def execute(self, context):
- return {'FINISHED'}
-
- def invoke(self, context, event):
- wm = context.window_manager
- return wm.invoke_props_dialog(self, width=300)
-
-
-class LoginPopupDialog(bpy.types.Operator):
- """Popup a dialog which enables the user to log in after being logged out automatically."""
- bl_idname = "wm.blenderkit_login_dialog"
- bl_label = "BlenderKit login"
- bl_options = {'REGISTER', 'INTERNAL'}
-
- message: bpy.props.StringProperty(
- name="Message",
- description="",
- default="Your were logged out from BlenderKit. Please login again. ")
-
- # @classmethod
- # def poll(cls, context):
- # return bpy.context.view_layer.objects.active is not None
-
- def draw(self, context):
- layout = self.layout
- utils.label_multiline(layout, text=self.message)
-
- layout.active_default = True
- op = layout.operator
- op = layout.operator("wm.url_open", text=self.link_text, icon='QUESTION')
- op.url = self.url
-
- def execute(self, context):
- # start_thumbnailer(self, context)
- return {'FINISHED'}
-
- def invoke(self, context, event):
- wm = context.window_manager
-
- return wm.invoke_props_dialog(self)
-
-
-def draw_panel_categories(self, context):
- s = context.scene
- ui_props = bpy.context.window_manager.blenderkitUI
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- layout = self.layout
- # row = layout.row()
- # row.prop(ui_props, 'asset_type', expand=True, icon_only=True)
- wm = bpy.context.window_manager
- if wm.get('bkit_categories') == None:
- return
- col = layout.column(align=True)
- if wm.get('active_category') is not None:
- acat = wm['active_category'][ui_props.asset_type]
- if len(acat) > 1:
- # we are in subcategory, so draw the parent button
- op = col.operator('view3d.blenderkit_set_category', text='...', icon='FILE_PARENT')
- op.asset_type = ui_props.asset_type
- op.category = ''
- cats = categories.get_category(wm['bkit_categories'], cat_path=acat)
- # draw freebies only in models parent category
- # if ui_props.asset_type == 'MODEL' and len(acat) == 1:
- # op = col.operator('view3d.blenderkit_asset_bar_widget', text='freebies')
- # op.free_only = True
-
- for c in cats['children']:
- if c['assetCount'] > 0 or (utils.profile_is_validator() and user_preferences.categories_fix):
- row = col.row(align=True)
- if len(c['children']) > 0 and c['assetCount'] > 15 or (
- utils.profile_is_validator() and user_preferences.categories_fix):
- row = row.split(factor=.8, align=True)
- # row = split.split()
- ctext = '%s (%i)' % (c['name'], c['assetCount'])
-
- preferences = bpy.context.preferences.addons['blenderkit'].preferences
- if 1:#preferences.experimental_features:
- op = row.operator('view3d.blenderkit_asset_bar_widget', text=ctext)
- else:
- op = row.operator('view3d.blenderkit_asset_bar', text=ctext)
- op.do_search = True
- op.keep_running = True
- op.tooltip = f"Browse {c['name']} category"
- op.category = c['slug']
- if len(c['children']) > 0 and c['assetCount'] > 15 or (
- utils.profile_is_validator() and user_preferences.categories_fix):
- # row = row.split()
- op = row.operator('view3d.blenderkit_set_category', text='>>')
- op.asset_type = ui_props.asset_type
- op.category = c['slug']
- # for c1 in c['children']:
- # if c1['assetCount']>0:
- # row = col.row()
- # split = row.split(percentage=.2)
- # row = split.split()
- # row = split.split()
- # ctext = '%s (%i)' % (c1['name'], c1['assetCount'])
- # op = row.operator('view3d.blenderkit_search', text=ctext)
- # op.category = c1['slug']
-
-
-class VIEW3D_PT_blenderkit_downloads(Panel):
- bl_category = "BlenderKit"
- bl_idname = "VIEW3D_PT_blenderkit_downloads"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_label = "Downloads"
-
- @classmethod
- def poll(cls, context):
- return len(download.download_threads) > 0
-
- def draw(self, context):
- layout = self.layout
- for i, threaddata in enumerate(download.download_threads):
- tcom = threaddata[2]
- asset_data = threaddata[1]
- row = layout.row()
- row.label(text=asset_data['name'])
- row.label(text=str(int(tcom.progress)) + ' %')
- op = row.operator('scene.blenderkit_download_kill', text='', icon='CANCEL')
- op.thread_index = i
- if tcom.passargs.get('retry_counter', 0) > 0:
- row = layout.row()
- row.label(text='failed. retrying ... ', icon='ERROR')
- row.label(text=str(tcom.passargs["retry_counter"]))
-
- layout.separator()
-
-
-def header_search_draw(self, context):
- '''Top bar menu in 3D view'''
-
- if not utils.guard_from_crash():
- return;
-
- preferences = bpy.context.preferences.addons['blenderkit'].preferences
- if not preferences.search_in_header:
- return
- if context.mode not in ('PAINT_TEXTURE', 'OBJECT', 'SCULPT'):
- return
-
- layout = self.layout
- s = bpy.context.scene
- wm = bpy.context.window_manager
- ui_props = bpy.context.window_manager.blenderkitUI
- if ui_props.asset_type == 'MODEL':
- props = wm.blenderkit_models
- if ui_props.asset_type == 'MATERIAL':
- props = wm.blenderkit_mat
- if ui_props.asset_type == 'BRUSH':
- props = wm.blenderkit_brush
- if ui_props.asset_type == 'HDR':
- props = wm.blenderkit_HDR
- if ui_props.asset_type == 'SCENE':
- props = wm.blenderkit_scene
-
- # the center snap menu is in edit and object mode if tool settings are off.
- # if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'):
- # layout.separator_spacer()
- layout.prop(ui_props, "asset_type", expand=True, icon_only=True, text='', icon='URL')
- layout.prop(props, "search_keywords", text="", icon='VIEWZOOM')
- draw_assetbar_show_hide(layout, props)
- layout.popover(panel="VIEW3D_PT_blenderkit_categories", text="", icon='OUTLINER')
-
- pcoll = icons.icon_collections["main"]
-
- if props.use_filters:
- icon_id = pcoll['filter_active'].icon_id
- else:
- icon_id = pcoll['filter'].icon_id
-
- if ui_props.asset_type == 'MODEL':
- layout.popover(panel="VIEW3D_PT_blenderkit_advanced_model_search", text="", icon_value=icon_id)
-
- elif ui_props.asset_type == 'MATERIAL':
- layout.popover(panel="VIEW3D_PT_blenderkit_advanced_material_search", text="", icon_value=icon_id)
- elif ui_props.asset_type == 'HDR':
- layout.popover(panel="VIEW3D_PT_blenderkit_advanced_HDR_search", text="", icon_value=icon_id)
-
- notifications = bpy.context.window_manager.get('bkit notifications')
- if notifications is not None and notifications['count'] > 0:
- layout.operator('wm.show_notifications', text="", icon_value=pcoll['bell'].icon_id)
- # layout.popover(panel="VIEW3D_PT_blenderkit_notifications", text="", icon_value=pcoll['bell'].icon_id)
-
- if utils.profile_is_validator():
- search_props = utils.get_search_props()
- layout.prop(search_props, 'search_verification_status', text='')
-
-
-def ui_message(title, message):
- def draw_message(self, context):
- layout = self.layout
- utils.label_multiline(layout, text=message, width=400)
-
- bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
-
-
-# We can store multiple preview collections here,
-# however in this example we only store "main"
-preview_collections = {}
-
-classes = (
- SetCategoryOperator,
- VIEW3D_PT_blenderkit_profile,
- VIEW3D_PT_blenderkit_login,
- VIEW3D_PT_blenderkit_notifications,
- VIEW3D_PT_blenderkit_unified,
- VIEW3D_PT_blenderkit_advanced_model_search,
- VIEW3D_PT_blenderkit_advanced_material_search,
- VIEW3D_PT_blenderkit_advanced_HDR_search,
- VIEW3D_PT_blenderkit_categories,
- VIEW3D_PT_blenderkit_import_settings,
- VIEW3D_PT_blenderkit_model_properties,
- NODE_PT_blenderkit_material_properties,
- # VIEW3D_PT_blenderkit_ratings,
- VIEW3D_PT_blenderkit_downloads,
- # OBJECT_MT_blenderkit_resolution_menu,
- OBJECT_MT_blenderkit_asset_menu,
- OBJECT_MT_blenderkit_login_menu,
- AssetPopupCard,
- UrlPopupDialog,
- ClosePopupButton,
- BlenderKitWelcomeOperator,
- MarkNotificationRead,
- LikeComment,
- ShowNotifications,
- NotificationOpenTarget,
- MarkAllNotificationsRead,
-)
-
-
-def header_draw(self, context):
- layout = self.layout
-
- self.draw_tool_settings(context)
-
- layout.separator_spacer()
- header_search_draw(self,context)
- layout.separator_spacer()
-
- self.draw_mode_settings(context)
-
-
-def register_ui_panels():
- for c in classes:
- bpy.utils.register_class(c)
-
- bpy.types.VIEW3D_HT_tool_header.draw = header_draw
- # bpy.types.VIEW3D_HT_tool_header.append(header_search_draw)
- # bpy.types.VIEW3D_MT_editor_menus.append(header_search_draw)
-
-
-def unregister_ui_panels():
- bpy.types.VIEW3D_HT_tool_header.remove(header_search_draw)
- # bpy.types.VIEW3D_MT_editor_menus.remove(header_search_draw)
- for c in classes:
- # print('unregister', c)
- bpy.utils.unregister_class(c)