Vilem Duha
29db4b5feb
was actually showing slugs, now shows names with links fix long descriptions issue - now has a 'more' button to read the rest online (label_multiline now has a max_lines parameter and returns True if max lenght was reached) fix avatars to match server
2203 lines
83 KiB
Python
2203 lines
83 KiB
Python
# ##### 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, 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
|
||
|
||
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.scene.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
|
||
props = s.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 = s.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 preferences.experimental_features:
|
||
op = layout.operator('view3d.blenderkit_asset_bar_widget', text='', icon=icon)
|
||
op.keep_running = False
|
||
op.do_search = False
|
||
op.tooltip = ttip
|
||
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):
|
||
s = context.scene
|
||
|
||
props = s.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):
|
||
s = context.scene
|
||
props = s.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 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.scene
|
||
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):
|
||
s = context.scene
|
||
props = s.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 = s.blenderkitUI
|
||
return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MODEL'
|
||
|
||
def draw(self, context):
|
||
s = context.scene
|
||
|
||
props = s.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, "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 = s.blenderkitUI
|
||
return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MATERIAL'
|
||
|
||
def draw(self, context):
|
||
s = context.scene
|
||
|
||
props = s.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')
|
||
|
||
|
||
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 = s.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):
|
||
s = context.scene
|
||
props = s.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 = s.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
|
||
ui_props = s.blenderkitUI
|
||
|
||
if ui_props.asset_type == 'MODEL':
|
||
# noinspection PyCallByClass
|
||
props = s.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 = s.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 = s.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 = s.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
|
||
print('running search no')
|
||
ui_props = bpy.context.scene.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]
|
||
|
||
bpy.context.scene.blenderkit_mat.search_keywords = ''#random_search[1]
|
||
bpy.context.scene.blenderkit_mat.search_keywords = '+is_free:true+score_gte:1000+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.scene.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='Rate')
|
||
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.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')
|
||
|
||
# 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')
|
||
op.asset_id = asset_data['id']
|
||
op.asset_type = asset_data['assetType']
|
||
|
||
if asset_data['assetType'] == 'model':
|
||
op = layout.operator('object.blenderkit_regenerate_thumbnail', text='Regenerate thumbnail')
|
||
op.asset_index = ui_props.active_index
|
||
|
||
if 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.scene.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.scene.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.scene.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:
|
||
s = str(round(s))
|
||
else:
|
||
s = '-'
|
||
return s
|
||
|
||
|
||
def push_op_left(layout, strength =5):
|
||
for a in range(0, strength):
|
||
layout.label(text='')
|
||
|
||
|
||
def label_or_url(layout, text='', tooltip='', url='', icon_value=None, icon=None):
|
||
'''automatically switch between different layout options for linking or tooltips'''
|
||
layout.emboss = 'NONE'
|
||
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)
|
||
|
||
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)
|
||
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=''):
|
||
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(split, text=right, tooltip=tooltip, url=url, 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=''):
|
||
parameter = utils.get_param(self.asset_data, key)
|
||
if parameter == None:
|
||
return
|
||
if type(parameter) == int:
|
||
parameter = f"{parameter:,d}"
|
||
elif type(parameter) == float:
|
||
parameter = f"{parameter:,.1f}"
|
||
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']
|
||
|
||
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')
|
||
self.draw_asset_parameter(box, key='manufacturer', pretext='Manufacturer') # TODO make them clickable!
|
||
self.draw_asset_parameter(box, key='designCollection', pretext='Collection')
|
||
self.draw_asset_parameter(box, key='designVariant', pretext='Variant')
|
||
self.draw_asset_parameter(box, key='designYear', pretext='Design year')
|
||
|
||
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')
|
||
self.draw_asset_parameter(box, key='material_style', pretext='Style')
|
||
self.draw_asset_parameter(box, key='model_style', pretext='Style')
|
||
|
||
if utils.get_param(self.asset_data, 'dimensionX'):
|
||
t = '%s×%s×%s m' % (utils.fmt_length(mparams['dimensionX']),
|
||
utils.fmt_length(mparams['dimensionY']),
|
||
utils.fmt_length(mparams['dimensionZ']))
|
||
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)
|
||
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) > 25:
|
||
text = url[:25] + '...'
|
||
else:
|
||
url = paths.get_author_gallery_url(a['id'])
|
||
text = "Open 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.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)
|
||
|
||
# row = box_thumbnail.row()
|
||
# row.scale_y = 3
|
||
# op = row.operator('view3d.asset_drag_drop', text='Drag & Drop from here', depress=True)
|
||
|
||
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 averaged user ratings. \n\n' \
|
||
'Score = quality × 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']} ratings" \
|
||
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, average from {rc['workingHours']} ratings" \
|
||
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.")
|
||
|
||
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))
|
||
|
||
def draw(self, context):
|
||
ui_props = context.scene.blenderkitUI
|
||
|
||
sr = bpy.context.window_manager['search results']
|
||
asset_data = sr[ui_props.active_index]
|
||
self.asset_data = asset_data
|
||
layout = self.layout
|
||
# top draggabe bar with name of the asset
|
||
top_row = layout.row()
|
||
top_drag_bar = top_row.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 = 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', 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)
|
||
# for i,c in enumerate(cat_path_names):
|
||
# cat_path_names[i] = c.capitalize()
|
||
# cat_path_names_string = ' > '.join(cat_path_names)
|
||
# # box.label(text=cat_path)
|
||
#
|
||
#
|
||
#
|
||
#
|
||
# # name_row.label(text=' ')
|
||
# top_drag_bar.label(text=f'{cat_path_names_string} > {aname}')
|
||
|
||
# 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=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)
|
||
|
||
def execute(self, context):
|
||
wm = context.window_manager
|
||
ui_props = context.scene.blenderkitUI
|
||
ui_props.draw_tooltip = False
|
||
sr = bpy.context.window_manager['search results']
|
||
asset_data = sr[ui_props.active_index]
|
||
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.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()
|
||
|
||
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 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):
|
||
# start_thumbnailer(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 = s.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', 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 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 preferences.search_in_header:
|
||
layout = self.layout
|
||
s = bpy.context.scene
|
||
ui_props = s.blenderkitUI
|
||
if ui_props.asset_type == 'MODEL':
|
||
props = s.blenderkit_models
|
||
if ui_props.asset_type == 'MATERIAL':
|
||
props = s.blenderkit_mat
|
||
if ui_props.asset_type == 'BRUSH':
|
||
props = s.blenderkit_brush
|
||
if ui_props.asset_type == 'HDR':
|
||
props = s.blenderkit_HDR
|
||
if ui_props.asset_type == 'SCENE':
|
||
props = s.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)
|
||
|
||
|
||
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_unified,
|
||
VIEW3D_PT_blenderkit_advanced_model_search,
|
||
VIEW3D_PT_blenderkit_advanced_material_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,
|
||
BlenderKitWelcomeOperator,
|
||
)
|
||
|
||
|
||
def register_ui_panels():
|
||
for c in classes:
|
||
bpy.utils.register_class(c)
|
||
bpy.types.VIEW3D_MT_editor_menus.append(header_search_draw)
|
||
|
||
|
||
def unregister_ui_panels():
|
||
bpy.types.VIEW3D_MT_editor_menus.remove(header_search_draw)
|
||
for c in classes:
|
||
# print('unregister', c)
|
||
bpy.utils.unregister_class(c)
|