Merge branch 'master' into temp-xr-actions-D9124
|
@ -49,6 +49,7 @@ if "bpy" in locals():
|
|||
overrides = reload(overrides)
|
||||
paths = reload(paths)
|
||||
ratings = reload(ratings)
|
||||
ratings_utils = reload(ratings_utils)
|
||||
resolutions = reload(resolutions)
|
||||
search = reload(search)
|
||||
tasks_queue = reload(tasks_queue)
|
||||
|
@ -84,6 +85,7 @@ else:
|
|||
from blenderkit import overrides
|
||||
from blenderkit import paths
|
||||
from blenderkit import ratings
|
||||
from blenderkit import ratings_utils
|
||||
from blenderkit import resolutions
|
||||
from blenderkit import search
|
||||
from blenderkit import tasks_queue
|
||||
|
@ -678,8 +680,8 @@ class BlenderKitCommonUploadProps(object):
|
|||
is_free: EnumProperty(
|
||||
name="Thumbnail Style",
|
||||
items=(
|
||||
('FREE', 'Free', "You consent you want to release this asset as free for everyone."),
|
||||
('FULL', 'Full', "Your asset will be only available for subscribers")
|
||||
('FULL', 'Full', "Your asset will be only available for subscribers"),
|
||||
('FREE', 'Free', "You consent you want to release this asset as free for everyone.")
|
||||
),
|
||||
description="Assets can be in Free or in Full plan. Also free assets generate credits.",
|
||||
default="FULL",
|
||||
|
@ -731,20 +733,20 @@ class BlenderKitRatingProps(PropertyGroup):
|
|||
description="quality of the material",
|
||||
default=0,
|
||||
min=-1, max=10,
|
||||
update=ratings.update_ratings_quality)
|
||||
update=ratings_utils.update_ratings_quality)
|
||||
|
||||
# the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily.
|
||||
rating_quality_ui: EnumProperty(name='rating_quality_ui',
|
||||
items=ratings.stars_enum_callback,
|
||||
items=ratings_utils.stars_enum_callback,
|
||||
description='Rating stars 0 - 10',
|
||||
default=None,
|
||||
update=ratings.update_quality_ui,
|
||||
update=ratings_utils.update_quality_ui,
|
||||
)
|
||||
|
||||
rating_work_hours: FloatProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
default=0.00,
|
||||
min=0.0, max=150, update=ratings.update_ratings_work_hours
|
||||
min=0.0, max=150, update=ratings_utils.update_ratings_work_hours
|
||||
)
|
||||
|
||||
# rating_complexity: IntProperty(name="Complexity",
|
||||
|
@ -849,8 +851,8 @@ class BlenderKitMaterialUploadProps(PropertyGroup, BlenderKitCommonUploadProps):
|
|||
is_free: EnumProperty(
|
||||
name="Thumbnail Style",
|
||||
items=(
|
||||
('FREE', 'Free', "You consent you want to release this asset as free for everyone."),
|
||||
('FULL', 'Full', "Your asset will be only available for subscribers.")
|
||||
('FULL', 'Full', "Your asset will be only available for subscribers."),
|
||||
('FREE', 'Free', "You consent you want to release this asset as free for everyone.")
|
||||
),
|
||||
description="Assets can be in Free or in Full plan. Also free assets generate credits. \n"
|
||||
"All BlenderKit materials are free.",
|
||||
|
|
|
@ -225,9 +225,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
|
|||
self.asset_name = name_label
|
||||
self.tooltip_widgets.append(name_label)
|
||||
offset_y = 16 + self.margin
|
||||
label = self.new_text('Left click or drag to append/link. Right click for more options.', self.assetbar_margin*2, labels_start + offset_y,
|
||||
text_size=14)
|
||||
self.tooltip_widgets.append(label)
|
||||
# label = self.new_text('Left click or drag to append/link. Right click for more options.', self.assetbar_margin*2, labels_start + offset_y,
|
||||
# text_size=14)
|
||||
# self.tooltip_widgets.append(label)
|
||||
|
||||
|
||||
self.hide_tooltip()
|
||||
|
@ -505,6 +505,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
|
|||
# handlers
|
||||
|
||||
def enter_button(self, widget):
|
||||
# context.window.cursor_warp(event.mouse_x, event.mouse_y - 20);
|
||||
|
||||
|
||||
self.show_tooltip()
|
||||
|
||||
if self.active_index != widget.search_index:
|
||||
|
@ -532,6 +535,8 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
|
|||
|
||||
self.tooltip_panel.update(tooltip_x, widget.y_screen + widget.height)
|
||||
self.tooltip_panel.layout_widgets()
|
||||
# bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT')
|
||||
|
||||
|
||||
def exit_button(self, widget):
|
||||
# this condition checks if there wasn't another button already entered, which can happen with small button gaps
|
||||
|
|
|
@ -156,7 +156,7 @@ def start_thumbnailer(self=None, json_args=None, props=None, wait=False, add_bg_
|
|||
eval_path_state = "bpy.data.objects['%s'].blenderkit.thumbnail_generating_state" % json_args['asset_name']
|
||||
eval_path = "bpy.data.objects['%s']" % json_args['asset_name']
|
||||
|
||||
bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state,
|
||||
bg_blender.add_bg_process(name = f"{json_args['asset_name']} thumbnailer" ,eval_path_computing=eval_path_computing, eval_path_state=eval_path_state,
|
||||
eval_path=eval_path, process_type='THUMBNAILER', process=proc)
|
||||
|
||||
|
||||
|
@ -206,7 +206,7 @@ def start_material_thumbnailer(self=None, json_args=None, props=None, wait=False
|
|||
eval_path_state = "bpy.data.materials['%s'].blenderkit.thumbnail_generating_state" % json_args['asset_name']
|
||||
eval_path = "bpy.data.materials['%s']" % json_args['asset_name']
|
||||
|
||||
bg_blender.add_bg_process(name=json_args['asset_name'], eval_path_computing=eval_path_computing,
|
||||
bg_blender.add_bg_process(name=f"{json_args['asset_name']} thumbnailer", eval_path_computing=eval_path_computing,
|
||||
eval_path_state=eval_path_state,
|
||||
eval_path=eval_path, process_type='THUMBNAILER', process=proc)
|
||||
if props:
|
||||
|
@ -328,7 +328,10 @@ class GenerateThumbnailOperator(bpy.types.Operator):
|
|||
|
||||
|
||||
class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
"""Generate Cycles thumbnail for model assets"""
|
||||
"""
|
||||
Generate default thumbnail with Cycles renderer and upload it.
|
||||
Works also for assets from search results, without being downloaded before.
|
||||
"""
|
||||
bl_idname = "object.blenderkit_regenerate_thumbnail"
|
||||
bl_label = "BlenderKit Thumbnail Re-generate"
|
||||
bl_options = {'REGISTER', 'INTERNAL'}
|
||||
|
@ -371,11 +374,9 @@ class ReGenerateThumbnailOperator(bpy.types.Operator):
|
|||
return True # bpy.context.view_layer.objects.active is not None
|
||||
|
||||
def draw(self, context):
|
||||
ob = bpy.context.active_object
|
||||
while ob.parent is not None:
|
||||
ob = ob.parent
|
||||
props = self
|
||||
layout = self.layout
|
||||
# layout.label('This will re-generate thumbnail and directly upload it to server. You should see your updated thumbnail online depending ')
|
||||
layout.label(text='thumbnailer settings')
|
||||
layout.prop(props, 'thumbnail_background_lightness')
|
||||
layout.prop(props, 'thumbnail_angle')
|
||||
|
@ -521,8 +522,8 @@ class GenerateMaterialThumbnailOperator(bpy.types.Operator):
|
|||
|
||||
class ReGenerateMaterialThumbnailOperator(bpy.types.Operator):
|
||||
"""
|
||||
Generate default thumbnail with Cycles renderer.
|
||||
Works also for assets from search results, without being downloaded before.
|
||||
Generate default thumbnail with Cycles renderer and upload it.
|
||||
Works also for assets from search results, without being downloaded before
|
||||
"""
|
||||
bl_idname = "object.blenderkit_regenerate_material_thumbnail"
|
||||
bl_label = "BlenderKit Material Thumbnail Re-Generator"
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
from blenderkit import utils, append_link, bg_blender, upload_bg, download
|
||||
|
||||
import sys, json, math
|
||||
import sys, json, math, os
|
||||
import bpy
|
||||
from pathlib import Path
|
||||
|
||||
|
@ -48,6 +48,10 @@ if __name__ == "__main__":
|
|||
data = json.load(s)
|
||||
# append_material(file_name, matname = None, link = False, fake_user = True)
|
||||
if data.get('do_download'):
|
||||
#need to save the file, so that asset doesn't get downloaded into addon directory
|
||||
temp_blend_path = os.path.join(data['tempdir'], 'temp.blend')
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temp_blend_path)
|
||||
|
||||
asset_data = data['asset_data']
|
||||
has_url = download.get_download_url(asset_data, download.get_scene_id(), user_preferences.api_key, tcom=None,
|
||||
resolution='blend')
|
||||
|
@ -80,8 +84,6 @@ if __name__ == "__main__":
|
|||
tscale = data["thumbnail_scale"]
|
||||
bpy.context.view_layer.objects['scaler'].scale = (tscale, tscale, tscale)
|
||||
bpy.context.view_layer.update()
|
||||
print('we have this materialB')
|
||||
print(mat)
|
||||
|
||||
for ob in bpy.context.visible_objects:
|
||||
if ob.name[:15] == 'MaterialPreview':
|
||||
|
@ -99,7 +101,6 @@ if __name__ == "__main__":
|
|||
if data["thumbnail_type"] in ['BALL', 'BALL_COMPLEX', 'CLOTH']:
|
||||
utils.automap(ob.name, tex_size = ts / tscale, just_scale = True, bg_exception=True)
|
||||
bpy.context.view_layer.update()
|
||||
print('got to C')
|
||||
|
||||
s.cycles.volume_step_size = tscale * .1
|
||||
|
||||
|
|
|
@ -18,10 +18,9 @@
|
|||
|
||||
|
||||
|
||||
from blenderkit import utils, append_link, bg_blender, download, upload_bg
|
||||
from blenderkit import utils, append_link, bg_blender, download, upload_bg, upload
|
||||
|
||||
import sys, json, math
|
||||
from pathlib import Path
|
||||
import sys, json, math, os
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
|
@ -78,7 +77,6 @@ def render_thumbnails():
|
|||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
print( 'got to A')
|
||||
with open(BLENDERKIT_EXPORT_DATA, 'r',encoding='utf-8') as s:
|
||||
data = json.load(s)
|
||||
|
||||
|
@ -86,8 +84,11 @@ if __name__ == "__main__":
|
|||
|
||||
|
||||
if data.get('do_download'):
|
||||
bg_blender.progress('Downloading asset')
|
||||
#need to save the file, so that asset doesn't get downloaded into addon directory
|
||||
temp_blend_path = os.path.join(data['tempdir'], 'temp.blend')
|
||||
bpy.ops.wm.save_as_mainfile(filepath = temp_blend_path)
|
||||
|
||||
bg_blender.progress('Downloading asset')
|
||||
asset_data = data['asset_data']
|
||||
has_url = download.get_download_url(asset_data, download.get_scene_id(), user_preferences.api_key, tcom=None,
|
||||
resolution='blend')
|
||||
|
@ -135,7 +136,6 @@ if __name__ == "__main__":
|
|||
}
|
||||
s = bpy.context.scene
|
||||
s.frame_set(fdict[data['thumbnail_angle']])
|
||||
print( 'got to C')
|
||||
|
||||
snapdict = {
|
||||
'GROUND': 'Ground',
|
||||
|
@ -155,7 +155,6 @@ if __name__ == "__main__":
|
|||
s.cycles.samples = data['thumbnail_samples']
|
||||
bpy.context.view_layer.cycles.use_denoising = data['thumbnail_denoising']
|
||||
bpy.context.view_layer.update()
|
||||
print( 'got to D')
|
||||
|
||||
# import blender's HDR here
|
||||
# hdr_path = Path('datafiles/studiolights/world/interior.exr')
|
||||
|
@ -179,9 +178,10 @@ if __name__ == "__main__":
|
|||
render_thumbnails()
|
||||
fpath = data['thumbnail_path'] + '.jpg'
|
||||
if data.get('upload_after_render') and data.get('asset_data'):
|
||||
# try to patch for the sake of older assets where thumbnail update doesn't work for the reasont
|
||||
# that original thumbnail files aren't available.
|
||||
# upload.patch_individual_metadata(data['asset_data']['id'], {}, user_preferences)
|
||||
bg_blender.progress('uploading thumbnail')
|
||||
preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
print('uploading A')
|
||||
file = {
|
||||
"type": "thumbnail",
|
||||
"index": 0,
|
||||
|
@ -189,20 +189,14 @@ if __name__ == "__main__":
|
|||
}
|
||||
upload_data = {
|
||||
"name": data['asset_data']['name'],
|
||||
"token": preferences.api_key,
|
||||
"token": user_preferences.api_key,
|
||||
"id": data['asset_data']['id']
|
||||
}
|
||||
print('uploading B')
|
||||
|
||||
upload_bg.upload_file(upload_data, file)
|
||||
print('uploading C')
|
||||
|
||||
bg_blender.progress('background autothumbnailer finished successfully')
|
||||
|
||||
|
||||
print( 'got to E')
|
||||
|
||||
|
||||
except:
|
||||
import traceback
|
||||
|
||||
|
|
|
@ -27,9 +27,24 @@ icon_collections = {}
|
|||
icons_read = {
|
||||
'fp.png': 'free',
|
||||
'flp.png': 'full',
|
||||
'test.jpg': 'test',
|
||||
'trophy.png': 'trophy',
|
||||
'dumbbell.png': 'dumbbell',
|
||||
'cc0.png': 'cc0',
|
||||
'royalty_free.png': 'royalty_free',
|
||||
}
|
||||
|
||||
verification_icons = {
|
||||
'vs_ready.png':'ready',
|
||||
'vs_deleted.png':'deleted' ,
|
||||
'vs_uploaded.png': 'uploaded',
|
||||
'vs_uploading.png': 'uploading',
|
||||
'vs_on_hold.png': 'on_hold',
|
||||
'vs_validated.png': 'validated',
|
||||
'vs_rejected.png': 'rejected'
|
||||
|
||||
}
|
||||
|
||||
icons_read.update(verification_icons)
|
||||
|
||||
def register_icons():
|
||||
# Note that preview collections returned by bpy.utils.previews
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
from blenderkit import paths, utils, rerequests, tasks_queue
|
||||
from blenderkit import paths, utils, rerequests, tasks_queue, ratings_utils
|
||||
|
||||
import bpy
|
||||
import requests, threading
|
||||
|
@ -50,36 +50,6 @@ def pretty_print_POST(req):
|
|||
))
|
||||
|
||||
|
||||
def upload_rating_thread(url, ratings, headers):
|
||||
''' Upload rating thread function / disconnected from blender data.'''
|
||||
bk_logger.debug('upload rating ' + url + str(ratings))
|
||||
for rating_name, score in ratings:
|
||||
if (score != -1 and score != 0):
|
||||
rating_url = url + rating_name + '/'
|
||||
data = {
|
||||
"score": score, # todo this kind of mixing is too much. Should have 2 bkit structures, upload, use
|
||||
}
|
||||
|
||||
try:
|
||||
r = rerequests.put(rating_url, data=data, verify=True, headers=headers)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print('ratings upload failed: %s' % str(e))
|
||||
|
||||
|
||||
def send_rating_to_thread_quality(url, ratings, headers):
|
||||
'''Sens rating into thread rating, main purpose is for tasks_queue.
|
||||
One function per property to avoid lost data due to stashing.'''
|
||||
thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
|
||||
thread.start()
|
||||
|
||||
|
||||
def send_rating_to_thread_work_hours(url, ratings, headers):
|
||||
'''Sens rating into thread rating, main purpose is for tasks_queue.
|
||||
One function per property to avoid lost data due to stashing.'''
|
||||
thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
|
||||
thread.start()
|
||||
|
||||
|
||||
def upload_review_thread(url, reviews, headers):
|
||||
r = rerequests.put(url, data=reviews, verify=True, headers=headers)
|
||||
|
@ -103,43 +73,6 @@ def get_rating(asset_id):
|
|||
print(r.text)
|
||||
|
||||
|
||||
def update_ratings_quality(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
api_key = user_preferences.api_key
|
||||
|
||||
headers = utils.get_headers(api_key)
|
||||
asset = self.id_data
|
||||
if asset:
|
||||
bkit_ratings = asset.bkit_ratings
|
||||
url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
|
||||
else:
|
||||
# this part is for operator rating:
|
||||
bkit_ratings = self
|
||||
url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
|
||||
|
||||
if bkit_ratings.rating_quality > 0.1:
|
||||
ratings = [('quality', bkit_ratings.rating_quality)]
|
||||
tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
|
||||
|
||||
def update_ratings_work_hours(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
api_key = user_preferences.api_key
|
||||
headers = utils.get_headers(api_key)
|
||||
asset = self.id_data
|
||||
if asset:
|
||||
bkit_ratings = asset.bkit_ratings
|
||||
url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
|
||||
else:
|
||||
# this part is for operator rating:
|
||||
bkit_ratings = self
|
||||
url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
|
||||
|
||||
if bkit_ratings.rating_work_hours > 0.45:
|
||||
ratings = [('working_hours', round(bkit_ratings.rating_work_hours, 1))]
|
||||
tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
|
||||
|
||||
def upload_rating(asset):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
api_key = user_preferences.api_key
|
||||
|
@ -155,12 +88,12 @@ def upload_rating(asset):
|
|||
|
||||
if bkit_ratings.rating_quality > 0.1:
|
||||
ratings = (('quality', bkit_ratings.rating_quality),)
|
||||
tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
tasks_queue.add_task((ratings_utils.send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
if bkit_ratings.rating_work_hours > 0.1:
|
||||
ratings = (('working_hours', round(bkit_ratings.rating_work_hours, 1)),)
|
||||
tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
tasks_queue.add_task((ratings_utils.send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
|
||||
thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
|
||||
thread = threading.Thread(target=ratings_utils.upload_rating_thread, args=(url, ratings, headers))
|
||||
thread.start()
|
||||
|
||||
url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/review'
|
||||
|
@ -242,185 +175,47 @@ class UploadRatingOperator(bpy.types.Operator):
|
|||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
def stars_enum_callback(self, context):
|
||||
'''regenerates the enum property used to display rating stars, so that there are filled/empty stars correctly.'''
|
||||
items = []
|
||||
for a in range(0, 10):
|
||||
if self.rating_quality < a + 1:
|
||||
icon = 'SOLO_OFF'
|
||||
else:
|
||||
icon = 'SOLO_ON'
|
||||
# has to have something before the number in the value, otherwise fails on registration.
|
||||
items.append((f'{a + 1}', f'{a + 1}', '', icon, a + 1))
|
||||
return items
|
||||
def draw_ratings_menu(self, context, layout):
|
||||
col = layout.column()
|
||||
# layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
|
||||
row = col.row()
|
||||
row.prop(self, 'rating_quality_ui', expand=True, icon_only=True, emboss=False)
|
||||
# row.label(text=str(self.rating_quality))
|
||||
col.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.label(text=f"How many hours did this {self.asset_type} save you?")
|
||||
|
||||
if self.asset_type in ('model', 'scene'):
|
||||
row = layout.row()
|
||||
if utils.profile_is_validator():
|
||||
col.prop(self, 'rating_work_hours')
|
||||
row.prop(self, 'rating_work_hours_ui', expand=True, icon_only=False, emboss=True)
|
||||
if float(self.rating_work_hours_ui) > 100:
|
||||
utils.label_multiline(layout,
|
||||
text=f"\nThat's huge! please be sure to give such rating only to godly {self.asset_type}s.\n",
|
||||
width=500)
|
||||
elif float(self.rating_work_hours_ui) > 18:
|
||||
layout.separator()
|
||||
|
||||
utils.label_multiline(layout,
|
||||
text=f"\nThat's a lot! please be sure to give such rating only to amazing {self.asset_type}s.\n",
|
||||
width=500)
|
||||
|
||||
elif self.asset_type == 'hdr':
|
||||
row = layout.row()
|
||||
row.prop(self, 'rating_work_hours_ui_1_10', expand=True, icon_only=False, emboss=True)
|
||||
else:
|
||||
row = layout.row()
|
||||
row.prop(self, 'rating_work_hours_ui_1_5', expand=True, icon_only=False, emboss=True)
|
||||
|
||||
|
||||
def update_quality_ui(self, context):
|
||||
'''Converts the _ui the enum into actual quality number.'''
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_quality_ui = '0'
|
||||
self.rating_quality = int(self.rating_quality_ui)
|
||||
|
||||
|
||||
def update_ratings_work_hours_ui(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_work_hours_ui = '0'
|
||||
self.rating_work_hours = float(self.rating_work_hours_ui)
|
||||
|
||||
|
||||
def update_ratings_work_hours_ui_1_5(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_work_hours_ui_1_5 = '0'
|
||||
# print('updating 1-5')
|
||||
# print(float(self.rating_work_hours_ui_1_5))
|
||||
self.rating_work_hours = float(self.rating_work_hours_ui_1_5)
|
||||
|
||||
def update_ratings_work_hours_ui_1_10(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_work_hours_ui_1_5 = '0'
|
||||
# print('updating 1-5')
|
||||
# print(float(self.rating_work_hours_ui_1_5))
|
||||
self.rating_work_hours = float(self.rating_work_hours_ui_1_10)
|
||||
|
||||
|
||||
class FastRateMenu(Operator):
|
||||
class FastRateMenu(Operator, ratings_utils.RatingsProperties):
|
||||
"""Rating of the assets , also directly from the asset bar - without need to download assets"""
|
||||
bl_idname = "wm.blenderkit_menu_rating_upload"
|
||||
bl_label = "Rate asset"
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
|
||||
message: StringProperty(
|
||||
name="message",
|
||||
description="message",
|
||||
default="Rating asset",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
asset_id: StringProperty(
|
||||
name="Asset Base Id",
|
||||
description="Unique id of the asset (hidden)",
|
||||
default="",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
asset_name: StringProperty(
|
||||
name="Asset Name",
|
||||
description="Name of the asset (hidden)",
|
||||
default="",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
asset_type: StringProperty(
|
||||
name="Asset type",
|
||||
description="asset type",
|
||||
default="",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
rating_quality: IntProperty(name="Quality",
|
||||
description="quality of the material",
|
||||
default=0,
|
||||
min=-1, max=10,
|
||||
# update=update_ratings_quality,
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
# the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily.
|
||||
rating_quality_ui: EnumProperty(name='rating_quality_ui',
|
||||
items=stars_enum_callback,
|
||||
description='Rating stars 0 - 10',
|
||||
default=0,
|
||||
update=update_quality_ui,
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
rating_work_hours: FloatProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
default=0.00,
|
||||
min=0.0, max=300,
|
||||
# update=update_ratings_work_hours,
|
||||
options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
high_rating_warning = "This is a high rating, please be sure to give such rating only to amazing assets"
|
||||
|
||||
rating_work_hours_ui: EnumProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
items=[('0', '0', ''),
|
||||
('.5', '0.5', ''),
|
||||
('1', '1', ''),
|
||||
('2', '2', ''),
|
||||
('3', '3', ''),
|
||||
('4', '4', ''),
|
||||
('5', '5', ''),
|
||||
('6', '6', ''),
|
||||
('8', '8', ''),
|
||||
('10', '10', ''),
|
||||
('15', '15', ''),
|
||||
('20', '20', ''),
|
||||
('30', '30', high_rating_warning),
|
||||
('50', '50', high_rating_warning),
|
||||
('100', '100', high_rating_warning),
|
||||
('150', '150', high_rating_warning),
|
||||
('200', '200', high_rating_warning),
|
||||
('250', '250', high_rating_warning),
|
||||
],
|
||||
default='0', update=update_ratings_work_hours_ui,
|
||||
options = {'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
rating_work_hours_ui_1_5: EnumProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
items=[('0', '0', ''),
|
||||
('.2', '0.2', ''),
|
||||
('.5', '0.5', ''),
|
||||
('1', '1', ''),
|
||||
('2', '2', ''),
|
||||
('3', '3', ''),
|
||||
('4', '4', ''),
|
||||
('5', '5', '')
|
||||
],
|
||||
default='0',
|
||||
update=update_ratings_work_hours_ui_1_5,
|
||||
options = {'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
rating_work_hours_ui_1_10: EnumProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
items=[('0', '0', ''),
|
||||
('1', '1', ''),
|
||||
('2', '2', ''),
|
||||
('3', '3', ''),
|
||||
('4', '4', ''),
|
||||
('5', '5', ''),
|
||||
('6', '6', ''),
|
||||
('7', '7', ''),
|
||||
('8', '8', ''),
|
||||
('9', '9', ''),
|
||||
('10', '10', '')
|
||||
],
|
||||
default='0',
|
||||
update=update_ratings_work_hours_ui_1_10,
|
||||
options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@ -430,41 +225,9 @@ class FastRateMenu(Operator):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
|
||||
# layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
|
||||
col.label(text=self.message)
|
||||
row = col.row()
|
||||
row.prop(self, 'rating_quality_ui', expand=True, icon_only=True, emboss=False)
|
||||
# row.label(text=str(self.rating_quality))
|
||||
col.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.label(text=f"How many hours did this {self.asset_type} save you?")
|
||||
|
||||
if self.asset_type in ('model', 'scene'):
|
||||
row = layout.row()
|
||||
if utils.profile_is_validator():
|
||||
col.prop(self, 'rating_work_hours')
|
||||
row.prop(self, 'rating_work_hours_ui', expand=True, icon_only=False, emboss=True)
|
||||
if float(self.rating_work_hours_ui) > 100:
|
||||
utils.label_multiline(layout,
|
||||
text=f"\nThat's huge! please be sure to give such rating only to godly {self.asset_type}s.\n",
|
||||
width=500)
|
||||
elif float(self.rating_work_hours_ui) > 18:
|
||||
layout.separator()
|
||||
|
||||
utils.label_multiline(layout,
|
||||
text=f"\nThat's a lot! please be sure to give such rating only to amazing {self.asset_type}s.\n",
|
||||
width=500)
|
||||
|
||||
elif self.asset_type == 'hdr':
|
||||
row = layout.row()
|
||||
row.prop(self, 'rating_work_hours_ui_1_10', expand=True, icon_only=False, emboss=True)
|
||||
else:
|
||||
row = layout.row()
|
||||
row.prop(self, 'rating_work_hours_ui_1_5', expand=True, icon_only=False, emboss=True)
|
||||
layout.label(text=self.message)
|
||||
|
||||
draw_ratings_menu(self, context, layout)
|
||||
|
||||
def execute(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
|
@ -484,11 +247,11 @@ class FastRateMenu(Operator):
|
|||
|
||||
if self.rating_quality > 0.1:
|
||||
rtgs = (('quality', self.rating_quality),)
|
||||
tasks_queue.add_task((send_rating_to_thread_quality, (url, rtgs, headers)), wait=2.5, only_last=True)
|
||||
tasks_queue.add_task((ratings_utils.send_rating_to_thread_quality, (url, rtgs, headers)), wait=2.5, only_last=True)
|
||||
|
||||
if self.rating_work_hours > 0.45:
|
||||
rtgs = (('working_hours', round(self.rating_work_hours, 1)),)
|
||||
tasks_queue.add_task((send_rating_to_thread_work_hours, (url, rtgs, headers)), wait=2.5, only_last=True)
|
||||
tasks_queue.add_task((ratings_utils.send_rating_to_thread_work_hours, (url, rtgs, headers)), wait=2.5, only_last=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
@ -505,7 +268,7 @@ class FastRateMenu(Operator):
|
|||
self.message = f"Rate asset {self.asset_name}"
|
||||
wm = context.window_manager
|
||||
|
||||
if self.asset_type in ('model','scene'):
|
||||
if self.asset_type in ('model', 'scene'):
|
||||
# spawn a wider one for validators for the enum buttons
|
||||
return wm.invoke_props_dialog(self, width=500)
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
# ##### 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 #####
|
||||
|
||||
# mainly update functions and callbacks for ratings properties, here to avoid circular imports.
|
||||
import bpy
|
||||
from blenderkit import utils, paths, tasks_queue, rerequests
|
||||
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
PointerProperty,
|
||||
)
|
||||
|
||||
|
||||
import threading
|
||||
import requests
|
||||
import logging
|
||||
|
||||
bk_logger = logging.getLogger('blenderkit')
|
||||
|
||||
|
||||
def upload_rating_thread(url, ratings, headers):
|
||||
''' Upload rating thread function / disconnected from blender data.'''
|
||||
bk_logger.debug('upload rating ' + url + str(ratings))
|
||||
for rating_name, score in ratings:
|
||||
if (score != -1 and score != 0):
|
||||
rating_url = url + rating_name + '/'
|
||||
data = {
|
||||
"score": score, # todo this kind of mixing is too much. Should have 2 bkit structures, upload, use
|
||||
}
|
||||
|
||||
try:
|
||||
r = rerequests.put(rating_url, data=data, verify=True, headers=headers)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print('ratings upload failed: %s' % str(e))
|
||||
|
||||
|
||||
def send_rating_to_thread_quality(url, ratings, headers):
|
||||
'''Sens rating into thread rating, main purpose is for tasks_queue.
|
||||
One function per property to avoid lost data due to stashing.'''
|
||||
thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
|
||||
thread.start()
|
||||
|
||||
|
||||
def send_rating_to_thread_work_hours(url, ratings, headers):
|
||||
'''Sens rating into thread rating, main purpose is for tasks_queue.
|
||||
One function per property to avoid lost data due to stashing.'''
|
||||
thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
|
||||
thread.start()
|
||||
|
||||
|
||||
def update_ratings_quality(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
api_key = user_preferences.api_key
|
||||
|
||||
headers = utils.get_headers(api_key)
|
||||
asset = self.id_data
|
||||
if asset:
|
||||
bkit_ratings = asset.bkit_ratings
|
||||
url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
|
||||
else:
|
||||
# this part is for operator rating:
|
||||
bkit_ratings = self
|
||||
url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
|
||||
|
||||
if bkit_ratings.rating_quality > 0.1:
|
||||
ratings = [('quality', bkit_ratings.rating_quality)]
|
||||
tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
|
||||
|
||||
def update_ratings_work_hours(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
api_key = user_preferences.api_key
|
||||
headers = utils.get_headers(api_key)
|
||||
asset = self.id_data
|
||||
if asset:
|
||||
bkit_ratings = asset.bkit_ratings
|
||||
url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
|
||||
else:
|
||||
# this part is for operator rating:
|
||||
bkit_ratings = self
|
||||
url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
|
||||
|
||||
if bkit_ratings.rating_work_hours > 0.45:
|
||||
ratings = [('working_hours', round(bkit_ratings.rating_work_hours, 1))]
|
||||
tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True)
|
||||
|
||||
|
||||
def update_quality_ui(self, context):
|
||||
'''Converts the _ui the enum into actual quality number.'''
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_quality_ui = '0'
|
||||
self.rating_quality = int(self.rating_quality_ui)
|
||||
|
||||
|
||||
def update_ratings_work_hours_ui(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_work_hours_ui = '0'
|
||||
self.rating_work_hours = float(self.rating_work_hours_ui)
|
||||
|
||||
|
||||
def update_ratings_work_hours_ui_1_5(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_work_hours_ui_1_5 = '0'
|
||||
# print('updating 1-5')
|
||||
# print(float(self.rating_work_hours_ui_1_5))
|
||||
self.rating_work_hours = float(self.rating_work_hours_ui_1_5)
|
||||
|
||||
|
||||
def update_ratings_work_hours_ui_1_10(self, context):
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
if user_preferences.api_key == '':
|
||||
# ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
|
||||
# return
|
||||
bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
|
||||
message='Please login/signup to rate assets. Clicking OK takes you to web login.')
|
||||
# self.rating_work_hours_ui_1_5 = '0'
|
||||
# print('updating 1-5')
|
||||
# print(float(self.rating_work_hours_ui_1_5))
|
||||
self.rating_work_hours = float(self.rating_work_hours_ui_1_10)
|
||||
|
||||
|
||||
def stars_enum_callback(self, context):
|
||||
'''regenerates the enum property used to display rating stars, so that there are filled/empty stars correctly.'''
|
||||
items = []
|
||||
for a in range(0, 10):
|
||||
if self.rating_quality < a + 1:
|
||||
icon = 'SOLO_OFF'
|
||||
else:
|
||||
icon = 'SOLO_ON'
|
||||
# has to have something before the number in the value, otherwise fails on registration.
|
||||
items.append((f'{a + 1}', f'{a + 1}', '', icon, a + 1))
|
||||
return items
|
||||
|
||||
class RatingsProperties():
|
||||
message: StringProperty(
|
||||
name="message",
|
||||
description="message",
|
||||
default="Rating asset",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
asset_id: StringProperty(
|
||||
name="Asset Base Id",
|
||||
description="Unique id of the asset (hidden)",
|
||||
default="",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
asset_name: StringProperty(
|
||||
name="Asset Name",
|
||||
description="Name of the asset (hidden)",
|
||||
default="",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
asset_type: StringProperty(
|
||||
name="Asset type",
|
||||
description="asset type",
|
||||
default="",
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
rating_quality: IntProperty(name="Quality",
|
||||
description="quality of the material",
|
||||
default=0,
|
||||
min=-1, max=10,
|
||||
update=update_ratings_quality,
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
# the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily.
|
||||
rating_quality_ui: EnumProperty(name='rating_quality_ui',
|
||||
items=stars_enum_callback,
|
||||
description='Rating stars 0 - 10',
|
||||
default=0,
|
||||
update=update_quality_ui,
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
rating_work_hours: FloatProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
default=0.00,
|
||||
min=0.0, max=300,
|
||||
update=update_ratings_work_hours,
|
||||
options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
high_rating_warning = "This is a high rating, please be sure to give such rating only to amazing assets"
|
||||
|
||||
rating_work_hours_ui: EnumProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
items=[('0', '0', ''),
|
||||
('.5', '0.5', ''),
|
||||
('1', '1', ''),
|
||||
('2', '2', ''),
|
||||
('3', '3', ''),
|
||||
('4', '4', ''),
|
||||
('5', '5', ''),
|
||||
('6', '6', ''),
|
||||
('8', '8', ''),
|
||||
('10', '10', ''),
|
||||
('15', '15', ''),
|
||||
('20', '20', ''),
|
||||
('30', '30', high_rating_warning),
|
||||
('50', '50', high_rating_warning),
|
||||
('100', '100', high_rating_warning),
|
||||
('150', '150', high_rating_warning),
|
||||
('200', '200', high_rating_warning),
|
||||
('250', '250', high_rating_warning),
|
||||
],
|
||||
default='0', update=update_ratings_work_hours_ui,
|
||||
options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
rating_work_hours_ui_1_5: EnumProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
items=[('0', '0', ''),
|
||||
('.2', '0.2', ''),
|
||||
('.5', '0.5', ''),
|
||||
('1', '1', ''),
|
||||
('2', '2', ''),
|
||||
('3', '3', ''),
|
||||
('4', '4', ''),
|
||||
('5', '5', '')
|
||||
],
|
||||
default='0',
|
||||
update=update_ratings_work_hours_ui_1_5,
|
||||
options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
rating_work_hours_ui_1_10: EnumProperty(name="Work Hours",
|
||||
description="How many hours did this work take?",
|
||||
items=[('0', '0', ''),
|
||||
('1', '1', ''),
|
||||
('2', '2', ''),
|
||||
('3', '3', ''),
|
||||
('4', '4', ''),
|
||||
('5', '5', ''),
|
||||
('6', '6', ''),
|
||||
('7', '7', ''),
|
||||
('8', '8', ''),
|
||||
('9', '9', ''),
|
||||
('10', '10', '')
|
||||
],
|
||||
default='0',
|
||||
update=update_ratings_work_hours_ui_1_10,
|
||||
options={'SKIP_SAVE'}
|
||||
)
|
|
@ -191,7 +191,7 @@ def fetch_server_data():
|
|||
if api_key != '' and bpy.context.window_manager.get('bkit profile') == None:
|
||||
get_profile()
|
||||
if bpy.context.window_manager.get('bkit_categories') is None:
|
||||
categories.fetch_categories_thread(api_key, force = False)
|
||||
categories.fetch_categories_thread(api_key, force=False)
|
||||
|
||||
|
||||
first_time = True
|
||||
|
@ -238,7 +238,7 @@ def parse_result(r):
|
|||
# except:
|
||||
# utils.p('asset with no files-size')
|
||||
asset_type = r['assetType']
|
||||
if len(r['files']) > 0:#TODO remove this condition so all assets are parsed.
|
||||
if len(r['files']) > 0: # TODO remove this condition so all assets are parsed.
|
||||
get_author(r)
|
||||
|
||||
r['available_resolutions'] = []
|
||||
|
@ -262,7 +262,6 @@ def parse_result(r):
|
|||
# small_tname = paths.extract_filename_from_url(f['fileThumbnail'])
|
||||
# allthumbs.append(tname) # TODO just first thumb is used now.
|
||||
|
||||
|
||||
if f['fileType'] == 'blend':
|
||||
durl = f['downloadUrl'].split('?')[0]
|
||||
# fname = paths.extract_filename_from_url(f['filePath'])
|
||||
|
@ -270,7 +269,7 @@ def parse_result(r):
|
|||
if f['fileType'].find('resolution') > -1:
|
||||
r['available_resolutions'].append(resolutions.resolutions[f['fileType']])
|
||||
|
||||
#code for more thumbnails
|
||||
# code for more thumbnails
|
||||
# tdict = {}
|
||||
# for i, t in enumerate(allthumbs):
|
||||
# tdict['thumbnail_%i'] = t
|
||||
|
@ -436,8 +435,8 @@ def timer_update():
|
|||
|
||||
load_previews()
|
||||
ui_props = bpy.context.scene.blenderkitUI
|
||||
if len(result_field) < ui_props.scrolloffset or not(thread[0].params.get('get_next')):
|
||||
#jump back
|
||||
if len(result_field) < ui_props.scrolloffset or not (thread[0].params.get('get_next')):
|
||||
# jump back
|
||||
ui_props.scrolloffset = 0
|
||||
props.is_searching = False
|
||||
props.search_error = False
|
||||
|
@ -572,11 +571,6 @@ def writeblockm(tooltip, mdata, key='', pretext=None, width=40): # for longer t
|
|||
return tooltip
|
||||
|
||||
|
||||
def fmt_length(prop):
|
||||
prop = str(round(prop, 2))
|
||||
return prop
|
||||
|
||||
|
||||
def has(mdata, prop):
|
||||
if mdata.get(prop) is not None and mdata[prop] is not None and mdata[prop] is not False:
|
||||
return True
|
||||
|
@ -591,178 +585,30 @@ def generate_tooltip(mdata):
|
|||
else:
|
||||
mparams = mdata['parameters']
|
||||
t = ''
|
||||
t = writeblock(t, mdata['displayName'], width=col_w)
|
||||
t += '\n'
|
||||
|
||||
t = writeblockm(t, mdata, key='description', pretext='', width=col_w)
|
||||
if mdata['description'] != '':
|
||||
t += '\n'
|
||||
|
||||
bools = (('rig', None), ('animated', None), ('manifold', 'non-manifold'), ('scene', None), ('simulation', None),
|
||||
('uv', None))
|
||||
for b in bools:
|
||||
if mparams.get(b[0]):
|
||||
mdata['tags'].append(b[0])
|
||||
elif b[1] != None:
|
||||
mdata['tags'].append(b[1])
|
||||
|
||||
bools_data = ('adult',)
|
||||
for b in bools_data:
|
||||
if mdata.get(b) and mdata[b]:
|
||||
mdata['tags'].append(b)
|
||||
t = writeblockm(t, mparams, key='designer', pretext='Designer', width=col_w)
|
||||
t = writeblockm(t, mparams, key='manufacturer', pretext='Manufacturer', width=col_w)
|
||||
t = writeblockm(t, mparams, key='designCollection', pretext='Design collection', width=col_w)
|
||||
|
||||
# t = writeblockm(t, mparams, key='engines', pretext='engine', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='model_style', pretext='style', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='material_style', pretext='style', width = col_w)
|
||||
# t = writeblockm(t, mdata, key='tags', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='condition', pretext='condition', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='productionLevel', pretext='production level', width = col_w)
|
||||
if has(mdata, 'purePbr'):
|
||||
t = writeblockm(t, mparams, key='pbrType', pretext='Pbr', width=col_w)
|
||||
|
||||
t = writeblockm(t, mparams, key='designYear', pretext='Design year', width=col_w)
|
||||
|
||||
if has(mparams, 'dimensionX'):
|
||||
t += 'Size: %s x %s x %sm\n' % (fmt_length(mparams['dimensionX']),
|
||||
fmt_length(mparams['dimensionY']),
|
||||
fmt_length(mparams['dimensionZ']))
|
||||
if has(mparams, 'faceCount') and mdata['assetType'] == 'model':
|
||||
t += 'Face count: %s\n' % (mparams['faceCount'])
|
||||
# t += 'face count: %s, render: %s\n' % (mparams['faceCount'], mparams['faceCountRender'])
|
||||
|
||||
# write files size - this doesn't reflect true file size, since files size is computed from all asset files, including resolutions.
|
||||
# if mdata.get('filesSize'):
|
||||
# fs = utils.files_size_to_text(mdata['filesSize'])
|
||||
# t += f'files size: {fs}\n'
|
||||
|
||||
# t = writeblockm(t, mparams, key='meshPolyType', pretext='mesh type', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='objectCount', pretext='nubmber of objects', width = col_w)
|
||||
|
||||
# t = writeblockm(t, mparams, key='materials', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='modifiers', width = col_w)
|
||||
# t = writeblockm(t, mparams, key='shaders', width = col_w)
|
||||
|
||||
# if has(mparams, 'textureSizeMeters'):
|
||||
# t += 'Texture size: %s m\n' % fmt_length(mparams['textureSizeMeters'])
|
||||
|
||||
if has(mparams, 'textureResolutionMax') and mparams['textureResolutionMax'] > 0:
|
||||
if not mparams.get('textureResolutionMin'): # for HDR's
|
||||
t = writeblockm(t, mparams, key='textureResolutionMax', pretext='Resolution', width=col_w)
|
||||
elif mparams.get('textureResolutionMin') == mparams['textureResolutionMax']:
|
||||
t = writeblockm(t, mparams, key='textureResolutionMin', pretext='Texture resolution', width=col_w)
|
||||
else:
|
||||
t += 'Tex resolution: %i - %i\n' % (mparams.get('textureResolutionMin'), mparams['textureResolutionMax'])
|
||||
|
||||
if has(mparams, 'thumbnailScale'):
|
||||
t = writeblockm(t, mparams, key='thumbnailScale', pretext='Preview scale', width=col_w)
|
||||
|
||||
# t += 'uv: %s\n' % mdata['uv']
|
||||
t = writeblock(t, mdata['displayName'], width=int(col_w * .6))
|
||||
# t += '\n'
|
||||
if mdata.get('license') == 'cc_zero':
|
||||
t+= 'license: CC Zero\n'
|
||||
else:
|
||||
t+= 'license: Royalty free\n'
|
||||
# t = writeblockm(t, mdata, key='license', width=col_w)
|
||||
|
||||
fs = mdata.get('files')
|
||||
|
||||
if utils.profile_is_validator():
|
||||
if fs and len(fs) > 2:
|
||||
resolutions = 'Resolutions:'
|
||||
list.sort(fs, key=lambda f: f['fileType'])
|
||||
for f in fs:
|
||||
if f['fileType'].find('resolution') > -1:
|
||||
resolutions += f['fileType'][11:] + ' '
|
||||
resolutions += '\n'
|
||||
t += resolutions.replace('_', '.')
|
||||
|
||||
if mdata['isFree']:
|
||||
t += 'Free plan\n'
|
||||
else:
|
||||
t += 'Full plan\n'
|
||||
else:
|
||||
if fs:
|
||||
for f in fs:
|
||||
if f['fileType'].find('resolution') > -1:
|
||||
t += 'Asset has lower resolutions available\n'
|
||||
break;
|
||||
|
||||
# generator is for both upload preview and search, this is only after search
|
||||
# if mdata.get('versionNumber'):
|
||||
# # t = writeblockm(t, mdata, key='versionNumber', pretext='version', width = col_w)
|
||||
# a_id = mdata['author'].get('id')
|
||||
# if a_id != None:
|
||||
# adata = bpy.context.window_manager['bkit authors'].get(str(a_id))
|
||||
# if adata != None:
|
||||
# t += generate_author_textblock(adata)
|
||||
|
||||
t += '\n'
|
||||
rc = mdata.get('ratingsCount')
|
||||
if rc:
|
||||
t+='\n'
|
||||
if rc:
|
||||
rcount = min(rc['quality'], rc['workingHours'])
|
||||
else:
|
||||
rcount = 0
|
||||
|
||||
show_rating_threshold = 5
|
||||
|
||||
if rcount < show_rating_threshold and mdata['assetType'] != 'hdr':
|
||||
t += f"Only assets with enough ratings \nshow the rating value. Please rate.\n"
|
||||
if rc['quality'] >= show_rating_threshold:
|
||||
# t += f"{int(mdata['ratingsAverage']['quality']) * '*'}\n"
|
||||
t += f"* {round(mdata['ratingsAverage']['quality'],1)}\n"
|
||||
if rc['workingHours'] >= show_rating_threshold:
|
||||
t += f"Hours saved: {int(mdata['ratingsAverage']['workingHours'])}\n"
|
||||
if utils.profile_is_validator():
|
||||
t += f"Score: {int(mdata['score'])}\n"
|
||||
|
||||
t += f"Ratings count {rc['quality']}*/{rc['workingHours']}wh value " \
|
||||
f"{(mdata['ratingsAverage']['quality'],1)}*/{(mdata['ratingsAverage']['workingHours'],1)}wh\n"
|
||||
if len(t.split('\n')) < 11:
|
||||
t += '\n'
|
||||
t += get_random_tip(mdata)
|
||||
t += '\n'
|
||||
# t = writeblockm(t, mdata, key='description', pretext='', width=col_w)
|
||||
return t
|
||||
|
||||
|
||||
def get_random_tip(mdata):
|
||||
def get_random_tip():
|
||||
t = ''
|
||||
|
||||
tip = 'Tip: ' + random.choice(rtips)
|
||||
t = writeblock(t, tip)
|
||||
return t
|
||||
# at = mdata['assetType']
|
||||
# if at == 'brush' or at == 'texture':
|
||||
# t += 'click to link %s' % mdata['assetType']
|
||||
# if at == 'model' or at == 'material':
|
||||
# tips = ['Click or drag in scene to link/append %s' % mdata['assetType'],
|
||||
# "'A' key to search assets by same author",
|
||||
# "'W' key to open Authors webpage",
|
||||
# ]
|
||||
# tip = 'Tip: ' + random.choice(tips)
|
||||
# t = writeblock(t, tip)
|
||||
return t
|
||||
|
||||
|
||||
def generate_author_textblock(adata):
|
||||
t = '\n\n\n'
|
||||
t = ''
|
||||
|
||||
if adata not in (None, ''):
|
||||
col_w = 40
|
||||
col_w = 2000
|
||||
if len(adata['firstName'] + adata['lastName']) > 0:
|
||||
t = 'Author:\n'
|
||||
t += '%s %s\n' % (adata['firstName'], adata['lastName'])
|
||||
t = '%s %s\n' % (adata['firstName'], adata['lastName'])
|
||||
t += '\n'
|
||||
if adata.get('aboutMeUrl') is not None:
|
||||
t = writeblockm(t, adata, key='aboutMeUrl', pretext='', width=col_w)
|
||||
t += '\n'
|
||||
if adata.get('aboutMe') is not None:
|
||||
t = writeblockm(t, adata, key='aboutMe', pretext='', width=col_w)
|
||||
t += '\n'
|
||||
return t
|
||||
|
||||
|
||||
|
@ -904,7 +750,8 @@ def get_profile():
|
|||
thread.start()
|
||||
return a
|
||||
|
||||
def query_to_url(query = {}, params = {}):
|
||||
|
||||
def query_to_url(query={}, params={}):
|
||||
# build a new request
|
||||
url = paths.get_api_url() + 'search/'
|
||||
|
||||
|
@ -948,15 +795,17 @@ def query_to_url(query = {}, params = {}):
|
|||
urlquery = url + requeststring
|
||||
return urlquery
|
||||
|
||||
|
||||
def parse_html_formated_error(text):
|
||||
report = text[text.find('<title>') + 7: text.find('</title>')]
|
||||
|
||||
return report
|
||||
|
||||
|
||||
class Searcher(threading.Thread):
|
||||
query = None
|
||||
|
||||
def __init__(self, query, params, orig_result, tempdir = '', headers = None, urlquery = ''):
|
||||
def __init__(self, query, params, orig_result, tempdir='', headers=None, urlquery=''):
|
||||
super(Searcher, self).__init__()
|
||||
self.query = query
|
||||
self.params = params
|
||||
|
@ -979,13 +828,11 @@ class Searcher(threading.Thread):
|
|||
query = self.query
|
||||
params = self.params
|
||||
|
||||
|
||||
t = time.time()
|
||||
mt('search thread started')
|
||||
# tempdir = paths.get_temp_dir('%s_search' % query['asset_type'])
|
||||
# json_filepath = os.path.join(tempdir, '%s_searchresult.json' % query['asset_type'])
|
||||
|
||||
|
||||
rdata = {}
|
||||
rdata['results'] = []
|
||||
|
||||
|
@ -1034,8 +881,6 @@ class Searcher(threading.Thread):
|
|||
imgpath = os.path.join(self.tempdir, imgname)
|
||||
thumb_small_filepaths.append(imgpath)
|
||||
|
||||
|
||||
|
||||
if d["assetType"] == 'hdr':
|
||||
larege_thumb_url = d['thumbnailMiddleUrlNonsquared']
|
||||
|
||||
|
@ -1047,8 +892,6 @@ class Searcher(threading.Thread):
|
|||
imgpath = os.path.join(self.tempdir, imgname)
|
||||
thumb_full_filepaths.append(imgpath)
|
||||
|
||||
|
||||
|
||||
# for f in d['files']:
|
||||
# # TODO move validation of published assets to server, too manmy checks here.
|
||||
# if f['fileType'] == 'thumbnail' and f['fileThumbnail'] != None and f['fileThumbnailLarge'] != None:
|
||||
|
@ -1147,10 +990,10 @@ def build_query_common(query, props):
|
|||
if props.search_keywords != '':
|
||||
query_common["query"] = props.search_keywords
|
||||
|
||||
if props.search_verification_status != 'ALL':
|
||||
if props.search_verification_status != 'ALL' and utils.profile_is_validator():
|
||||
query_common['verification_status'] = props.search_verification_status.lower()
|
||||
|
||||
if props.unrated_only:
|
||||
if props.unrated_only and utils.profile_is_validator():
|
||||
query["quality_count"] = 0
|
||||
|
||||
if props.search_file_size:
|
||||
|
@ -1323,7 +1166,7 @@ def add_search_process(query, params, orig_result):
|
|||
if not params['get_next']:
|
||||
urlquery = query_to_url(query, params)
|
||||
|
||||
thread = Searcher(query, params, orig_result, tempdir = tempdir, headers = headers, urlquery = urlquery)
|
||||
thread = Searcher(query, params, orig_result, tempdir=tempdir, headers=headers, urlquery=urlquery)
|
||||
thread.start()
|
||||
|
||||
search_threads.append([thread, tempdir, query['asset_type'], {}]) # 4th field is for results
|
||||
|
@ -1517,17 +1360,20 @@ def search_update(self, context):
|
|||
|
||||
search()
|
||||
|
||||
|
||||
# accented_string is of type 'unicode'
|
||||
def strip_accents(s):
|
||||
return ''.join(c for c in unicodedata.normalize('NFD', s)
|
||||
if unicodedata.category(c) != 'Mn')
|
||||
|
||||
|
||||
class SearchOperator(Operator):
|
||||
"""Tooltip"""
|
||||
bl_idname = "view3d.blenderkit_search"
|
||||
bl_label = "BlenderKit asset search"
|
||||
bl_description = "Search online for assets"
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
|
||||
own: BoolProperty(name="own assets only",
|
||||
description="Find all own assets",
|
||||
default=False)
|
||||
|
@ -1559,6 +1405,12 @@ class SearchOperator(Operator):
|
|||
options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
tooltip: bpy.props.StringProperty(default='Runs search and displays the asset bar at the same time')
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
return properties.tooltip
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
@ -1571,15 +1423,50 @@ class SearchOperator(Operator):
|
|||
if self.keywords != '':
|
||||
sprops.search_keywords = self.keywords
|
||||
|
||||
|
||||
search(category=self.category, get_next=self.get_next, author_id=self.author_id)
|
||||
# bpy.ops.view3d.blenderkit_asset_bar()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class UrlOperator(Operator):
|
||||
""""""
|
||||
bl_idname = "wm.blenderkit_url"
|
||||
bl_label = ""
|
||||
bl_description = "Search online for assets"
|
||||
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')
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
return properties.tooltip
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.url_open(url=self.url)
|
||||
return {'FINISHED'}
|
||||
|
||||
class TooltipLabelOperator(Operator):
|
||||
""""""
|
||||
bl_idname = "wm.blenderkit_tooltip"
|
||||
bl_label = ""
|
||||
bl_description = "Empty operator to be able to create tooltips on labels in UI"
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
|
||||
tooltip: bpy.props.StringProperty(default='Open a web page')
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
return properties.tooltip
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = [
|
||||
SearchOperator
|
||||
SearchOperator,
|
||||
UrlOperator,
|
||||
TooltipLabelOperator
|
||||
]
|
||||
|
||||
|
||||
|
|
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 540 B |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
327
blenderkit/ui.py
|
@ -231,11 +231,6 @@ def draw_ratings_bgl():
|
|||
if rating_possible: # (not rated or ui_props.rating_menu_on):
|
||||
# print('rating is pssible', asset_data['name'])
|
||||
bkit_ratings = asset.bkit_ratings
|
||||
bgcol = bpy.context.preferences.themes[0].user_interface.wcol_tooltip.inner
|
||||
textcol = (1, 1, 1, 1)
|
||||
|
||||
r = bpy.context.region
|
||||
font_size = int(ui.rating_ui_scale * 20)
|
||||
|
||||
if ui.rating_button_on:
|
||||
# print('should draw button')
|
||||
|
@ -261,7 +256,6 @@ def draw_ratings_bgl():
|
|||
ui.rating_button_width,
|
||||
ui.rating_button_width,
|
||||
img, 1)
|
||||
# ui_bgl.draw_text( 'rate asset %s' % asset_data['name'],r.width - rating_button_width + margin, margin, font_size)
|
||||
return
|
||||
|
||||
|
||||
|
@ -278,50 +272,41 @@ def draw_text_block(x=0, y=0, width=40, font_size=10, line_height=15, text='', c
|
|||
ui_bgl.draw_text(l, x, ytext, font_size, color)
|
||||
|
||||
|
||||
def draw_tooltip(x, y, text='', author='', img=None, gravatar=None):
|
||||
def draw_tooltip(x, y, name='', author='', quality = '-', img=None, gravatar=None):
|
||||
region = bpy.context.region
|
||||
scale = bpy.context.preferences.view.ui_scale
|
||||
t = time.time()
|
||||
|
||||
ttipmargin = 5
|
||||
textmargin = 10
|
||||
|
||||
font_height = int(12 * scale)
|
||||
line_height = int(15 * scale)
|
||||
nameline_height = int(23 * scale)
|
||||
|
||||
lines = text.split('\n')
|
||||
alines = author.split('\n')
|
||||
ncolumns = 2
|
||||
# nlines = math.ceil((len(lines) - 1) / ncolumns)
|
||||
nlines = max(len(lines) - 1, len(alines)) # math.ceil((len(lines) - 1) / ncolumns)
|
||||
|
||||
texth = line_height * nlines + nameline_height
|
||||
|
||||
if not img or max(img.size[0], img.size[1]) == 0:
|
||||
return;
|
||||
isizex = int(512 * scale * img.size[0] / max(img.size[0], img.size[1]))
|
||||
isizey = int(512 * scale * img.size[1] / max(img.size[0], img.size[1]))
|
||||
|
||||
estimated_height = 2 * ttipmargin + textmargin + isizey
|
||||
x += 20
|
||||
y -= 20
|
||||
#first get image size scaled
|
||||
isizex = int(512 * scale * img.size[0] / min(img.size[0], img.size[1]))
|
||||
isizey = int(512 * scale * img.size[1] / min(img.size[0], img.size[1]))
|
||||
|
||||
ttipmargin = 5 * scale
|
||||
#then do recurrent re-scaling, to know where to fit the tooltip
|
||||
estimated_height = 2 * ttipmargin + isizey
|
||||
if estimated_height > y:
|
||||
scaledown = y / (estimated_height)
|
||||
scale *= scaledown
|
||||
# we need to scale these down to have correct size if the tooltip wouldn't fit.
|
||||
font_height = int(12 * scale)
|
||||
line_height = int(15 * scale)
|
||||
nameline_height = int(23 * scale)
|
||||
|
||||
lines = text.split('\n')
|
||||
isizex = int(512 * scale * img.size[0] / min(img.size[0], img.size[1]))
|
||||
isizey = int(512 * scale * img.size[1] / min(img.size[0], img.size[1]))
|
||||
|
||||
texth = line_height * nlines + nameline_height
|
||||
isizex = int(512 * scale * img.size[0] / max(img.size[0], img.size[1]))
|
||||
isizey = int(512 * scale * img.size[1] / max(img.size[0], img.size[1]))
|
||||
ttipmargin = 5 * scale
|
||||
textmargin = 12 * scale
|
||||
|
||||
name_height = int(18 * scale)
|
||||
if gravatar is not None:
|
||||
overlay_height_base = 90
|
||||
else:
|
||||
overlay_height_base = 70
|
||||
|
||||
x += 2 * ttipmargin
|
||||
y -= 2 * ttipmargin
|
||||
|
||||
overlay_height = overlay_height_base * scale
|
||||
name_height = int(20 * scale)
|
||||
|
||||
width = isizex + 2 * ttipmargin
|
||||
|
||||
|
@ -330,116 +315,58 @@ def draw_tooltip(x, y, text='', author='', img=None, gravatar=None):
|
|||
if r.type == 'UI':
|
||||
properties_width = r.width
|
||||
|
||||
# limit to area borders
|
||||
x = min(x + width, region.width - properties_width) - width
|
||||
|
||||
bgcol = bpy.context.preferences.themes[0].user_interface.wcol_tooltip.inner
|
||||
bgcol1 = (bgcol[0], bgcol[1], bgcol[2], .6)
|
||||
# define_colors
|
||||
background_color = bpy.context.preferences.themes[0].user_interface.wcol_tooltip.inner
|
||||
background_overlay = (background_color[0], background_color[1], background_color[2], .8)
|
||||
textcol = bpy.context.preferences.themes[0].user_interface.wcol_tooltip.text
|
||||
textcol = (textcol[0], textcol[1], textcol[2], 1)
|
||||
textcol_mild = (textcol[0] * .8, textcol[1] * .8, textcol[2] * .8, 1)
|
||||
textcol_strong = (textcol[0] * 1.3, textcol[1] * 2.3, textcol[2] * 1.3, 1)
|
||||
# textcol_strong = (0.4, 1, 0.3, 1)
|
||||
white = (1, 1, 1, .1)
|
||||
|
||||
# background
|
||||
ui_bgl.draw_rect(x - ttipmargin,
|
||||
y - 2 * ttipmargin - isizey,
|
||||
isizex + ttipmargin * 2,
|
||||
2 * ttipmargin + isizey,
|
||||
bgcol)
|
||||
background_color)
|
||||
|
||||
# main preview image
|
||||
ui_bgl.draw_image(x, y - isizey - ttipmargin, isizex, isizey, img, 1)
|
||||
|
||||
# text overlay background
|
||||
ui_bgl.draw_rect(x - ttipmargin,
|
||||
y - 2 * ttipmargin - isizey,
|
||||
isizex + ttipmargin * 2,
|
||||
2 * ttipmargin + texth,
|
||||
bgcol1)
|
||||
ttipmargin + overlay_height ,
|
||||
background_overlay)
|
||||
|
||||
#draw name
|
||||
name_x = x + textmargin
|
||||
name_y = y - isizey + overlay_height - textmargin - name_height
|
||||
ui_bgl.draw_text(name, name_x, name_y, name_height, textcol)
|
||||
|
||||
|
||||
# draw gravatar
|
||||
gsize = 40
|
||||
author_x_text = x + isizex - textmargin
|
||||
gravatar_size = overlay_height - 2 * textmargin
|
||||
gravatar_y = y - isizey - ttipmargin + textmargin
|
||||
if gravatar is not None:
|
||||
# ui_bgl.draw_image(x + isizex - gsize - textmargin, y - isizey + texth - gsize - nameline_height - textmargin,
|
||||
# gsize, gsize, gravatar, 1)
|
||||
ui_bgl.draw_image(x + isizex / 2 + textmargin, y - isizey + texth - gsize - nameline_height - textmargin,
|
||||
gsize, gsize, gravatar, 1)
|
||||
author_x_text -= gravatar_size + textmargin
|
||||
ui_bgl.draw_image(x + isizex - gravatar_size - textmargin,
|
||||
gravatar_y, # + textmargin,
|
||||
gravatar_size, gravatar_size, gravatar, 1)
|
||||
|
||||
i = 0
|
||||
column_lines = -1 # start minus one for the name
|
||||
xtext = x + textmargin
|
||||
fsize = name_height
|
||||
tcol = textcol
|
||||
#draw author's name
|
||||
author_text_size = int(name_height * .7)
|
||||
ui_bgl.draw_text(author, author_x_text, gravatar_y, author_text_size, textcol, ralign=True)
|
||||
|
||||
for l in lines:
|
||||
ytext = y - column_lines * line_height - nameline_height - ttipmargin - textmargin - isizey + texth
|
||||
if i == 0:
|
||||
ytext = y - name_height + 5 - isizey + texth - textmargin
|
||||
elif i == len(lines) - 1:
|
||||
ytext = y - (nlines - 1) * line_height - nameline_height - ttipmargin * 2 - isizey + texth
|
||||
tcol = textcol
|
||||
tsize = font_height
|
||||
else:
|
||||
fsize = font_height
|
||||
# draw quality
|
||||
quality_text_size = int(name_height * 1)
|
||||
img = utils.get_thumbnail('star_grey.png')
|
||||
ui_bgl.draw_image(name_x, gravatar_y, quality_text_size, quality_text_size, img, .6)
|
||||
ui_bgl.draw_text(str(quality), name_x + quality_text_size + 5, gravatar_y, quality_text_size, textcol)
|
||||
|
||||
if l[:4] == 'Tip:' or l[:11] == 'Please rate':
|
||||
tcol = textcol_strong
|
||||
fsize = font_height + 1
|
||||
|
||||
i += 1
|
||||
column_lines += 1
|
||||
ui_bgl.draw_text(l, xtext, ytext, fsize, tcol)
|
||||
xtext += int(isizex / ncolumns)
|
||||
|
||||
column_lines = 1
|
||||
i = 0
|
||||
for l in alines:
|
||||
if gravatar is not None:
|
||||
if column_lines == 1:
|
||||
xtext += gsize + textmargin
|
||||
if column_lines == 4:
|
||||
xtext -= gsize + textmargin
|
||||
|
||||
ytext = y - column_lines * line_height - nameline_height - ttipmargin - textmargin - isizey + texth
|
||||
if False: # i == 0:
|
||||
ytext = y - name_height + 5 - isizey + texth - textmargin
|
||||
elif i == len(lines) - 1:
|
||||
ytext = y - (nlines - 1) * line_height - nameline_height - ttipmargin * 2 - isizey + texth
|
||||
tcol = textcol
|
||||
tsize = font_height
|
||||
if (i > 0 and alines[i - 1][:7] == 'Author:'):
|
||||
tcol = textcol_strong
|
||||
fsize = font_height + 2
|
||||
else:
|
||||
fsize = font_height
|
||||
tcol = textcol
|
||||
|
||||
if l[:4] == 'Tip:' or l[:11] == 'Please rate':
|
||||
tcol = textcol_strong
|
||||
fsize = font_height + 1
|
||||
|
||||
i += 1
|
||||
column_lines += 1
|
||||
ui_bgl.draw_text(l, xtext, ytext, fsize, tcol)
|
||||
|
||||
# for l in lines:
|
||||
# if column_lines >= nlines:
|
||||
# xtext += int(isizex / ncolumns)
|
||||
# column_lines = 0
|
||||
# ytext = y - column_lines * line_height - nameline_height - ttipmargin - textmargin - isizey + texth
|
||||
# if i == 0:
|
||||
# ytext = y - name_height + 5 - isizey + texth - textmargin
|
||||
# elif i == len(lines) - 1:
|
||||
# ytext = y - (nlines - 1) * line_height - nameline_height - ttipmargin * 2 - isizey + texth
|
||||
# tcol = textcol
|
||||
# tsize = font_height
|
||||
# else:
|
||||
# if l[:4] == 'Tip:':
|
||||
# tcol = textcol_strong
|
||||
# fsize = font_height
|
||||
# i += 1
|
||||
# column_lines += 1
|
||||
# ui_bgl.draw_text(l, xtext, ytext, fsize, tcol)
|
||||
|
||||
t = time.time()
|
||||
|
||||
|
||||
def draw_tooltip_with_author(asset_data, x, y):
|
||||
|
@ -453,104 +380,24 @@ def draw_tooltip_with_author(asset_data, x, y):
|
|||
if a is not None and a != '':
|
||||
if a.get('gravatarImg') is not None:
|
||||
gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash'])
|
||||
atip = a['tooltip']
|
||||
|
||||
# scene = bpy.context.scene
|
||||
# ui_props = scene.blenderkitUI
|
||||
draw_tooltip(x, y, text=asset_data['tooltip'], author=atip, img=img,
|
||||
aname = asset_data['displayName']
|
||||
if len(aname)>36:
|
||||
aname = f"{aname[:33]}..."
|
||||
|
||||
rc = asset_data.get('ratingsCount')
|
||||
show_rating_threshold = 0
|
||||
rcount = 0
|
||||
quality = '-'
|
||||
if rc:
|
||||
rcount = min(rc['quality'], rc['workingHours'])
|
||||
if rcount>show_rating_threshold:
|
||||
quality = round(asset_data['ratingsAverage'].get('quality'))
|
||||
|
||||
draw_tooltip(x, y, name=aname, author=f"by {a['firstName']} {a['lastName']}", quality= quality, img=img,
|
||||
gravatar=gimg)
|
||||
|
||||
|
||||
def draw_tooltip_old(x, y, text='', author='', img=None):
|
||||
region = bpy.context.region
|
||||
scale = bpy.context.preferences.view.ui_scale
|
||||
t = time.time()
|
||||
|
||||
ttipmargin = 10
|
||||
|
||||
font_height = int(12 * scale)
|
||||
line_height = int(15 * scale)
|
||||
nameline_height = int(23 * scale)
|
||||
|
||||
lines = text.split('\n')
|
||||
ncolumns = 2
|
||||
nlines = math.ceil((len(lines) - 1) / ncolumns)
|
||||
texth = line_height * nlines + nameline_height
|
||||
|
||||
isizex = int(512 * scale * img.size[0] / max(img.size[0], img.size[1]))
|
||||
isizey = int(512 * scale * img.size[1] / max(img.size[0], img.size[1]))
|
||||
|
||||
estimated_height = texth + 3 * ttipmargin + isizey
|
||||
|
||||
if estimated_height > y:
|
||||
scaledown = y / (estimated_height)
|
||||
scale *= scaledown
|
||||
# we need to scale these down to have correct size if the tooltip wouldn't fit.
|
||||
font_height = int(12 * scale)
|
||||
line_height = int(15 * scale)
|
||||
nameline_height = int(23 * scale)
|
||||
|
||||
lines = text.split('\n')
|
||||
ncolumns = 2
|
||||
nlines = math.ceil((len(lines) - 1) / ncolumns)
|
||||
texth = line_height * nlines + nameline_height
|
||||
|
||||
isizex = int(512 * scale * img.size[0] / max(img.size[0], img.size[1]))
|
||||
isizey = int(512 * scale * img.size[1] / max(img.size[0], img.size[1]))
|
||||
|
||||
name_height = int(18 * scale)
|
||||
|
||||
x += 2 * ttipmargin
|
||||
y -= 2 * ttipmargin
|
||||
|
||||
width = isizex + 2 * ttipmargin
|
||||
|
||||
properties_width = 0
|
||||
for r in bpy.context.area.regions:
|
||||
if r.type == 'UI':
|
||||
properties_width = r.width
|
||||
|
||||
x = min(x + width, region.width - properties_width) - width
|
||||
|
||||
bgcol = bpy.context.preferences.themes[0].user_interface.wcol_tooltip.inner
|
||||
textcol = bpy.context.preferences.themes[0].user_interface.wcol_tooltip.text
|
||||
textcol = (textcol[0], textcol[1], textcol[2], 1)
|
||||
textcol1 = (textcol[0] * .8, textcol[1] * .8, textcol[2] * .8, 1)
|
||||
white = (1, 1, 1, .1)
|
||||
|
||||
ui_bgl.draw_rect(x - ttipmargin,
|
||||
y - texth - 2 * ttipmargin - isizey,
|
||||
isizex + ttipmargin * 2,
|
||||
texth + 3 * ttipmargin + isizey,
|
||||
bgcol)
|
||||
|
||||
i = 0
|
||||
column_lines = -1 # start minus one for the name
|
||||
xtext = x
|
||||
fsize = name_height
|
||||
tcol = textcol
|
||||
for l in lines:
|
||||
if column_lines >= nlines:
|
||||
xtext += int(isizex / ncolumns)
|
||||
column_lines = 0
|
||||
ytext = y - column_lines * line_height - nameline_height - ttipmargin
|
||||
if i == 0:
|
||||
ytext = y - name_height + 5
|
||||
elif i == len(lines) - 1:
|
||||
ytext = y - (nlines - 1) * line_height - nameline_height - ttipmargin
|
||||
tcol = textcol
|
||||
tsize = font_height
|
||||
else:
|
||||
if l[:5] == 'tags:':
|
||||
tcol = textcol1
|
||||
fsize = font_height
|
||||
i += 1
|
||||
column_lines += 1
|
||||
ui_bgl.draw_text(l, xtext, ytext, fsize, tcol)
|
||||
t = time.time()
|
||||
ui_bgl.draw_image(x, y - texth - isizey - ttipmargin, isizex, isizey, img, 1)
|
||||
|
||||
|
||||
def draw_callback_2d(self, context):
|
||||
if not utils.guard_from_crash():
|
||||
return
|
||||
|
@ -592,14 +439,6 @@ def draw_downloader(x, y, percent=0, img=None, text=''):
|
|||
# ui_bgl.draw_text(asset_data['filesSize'])
|
||||
if text:
|
||||
ui_bgl.draw_text(text, x, y - 15, 12, colors.TEXT)
|
||||
# asset_data and asset_data.get('filesSize'):
|
||||
# fs = asset_data['filesSize']
|
||||
# fsmb = fs // (1024 * 1024)
|
||||
# fskb = fs % 1024
|
||||
# if fsmb == 0:
|
||||
# t += 'files size: %iKB\n' % fskb
|
||||
# else:
|
||||
# t += 'files size: %iMB %iKB\n' % (fsmb, fskb)
|
||||
|
||||
|
||||
def draw_progress(x, y, text='', percent=None, color=colors.GREEN):
|
||||
|
@ -655,10 +494,10 @@ def draw_callback_2d_progress(self, context):
|
|||
|
||||
for process in bg_blender.bg_processes:
|
||||
tcom = process[1]
|
||||
n=''
|
||||
n = ''
|
||||
if tcom.name is not None:
|
||||
n = tcom.name +': '
|
||||
draw_progress(x, y - index * 30, '%s' % n+tcom.lasttext,
|
||||
n = tcom.name + ': '
|
||||
draw_progress(x, y - index * 30, '%s' % n + tcom.lasttext,
|
||||
tcom.progress)
|
||||
index += 1
|
||||
global reports
|
||||
|
@ -687,7 +526,7 @@ def draw_callback_2d_upload_preview(self, context):
|
|||
|
||||
img = utils.get_hidden_image(ui_props.thumbnail_image, 'upload_preview')
|
||||
|
||||
draw_tooltip(ui_props.bar_x, ui_props.bar_y, text=ui_props.tooltip, img=img)
|
||||
draw_tooltip(ui_props.bar_x, ui_props.bar_y, name=ui_props.tooltip, img=img)
|
||||
|
||||
|
||||
def is_upload_old(asset_data):
|
||||
|
@ -851,6 +690,8 @@ def draw_asset_bar(self, context):
|
|||
img = utils.get_thumbnail('locked.png')
|
||||
ui_bgl.draw_image(x + 2, y + 2, 24, 24, img, 1)
|
||||
|
||||
# pcoll = icons.icon_collections["main"]
|
||||
# v_icon = pcoll['rejected']
|
||||
v_icon = verification_icons[result.get('verificationStatus', 'validated')]
|
||||
if v_icon is not None:
|
||||
img = utils.get_thumbnail(v_icon)
|
||||
|
@ -1290,6 +1131,7 @@ class ParticlesDropDialog(bpy.types.Operator):
|
|||
# wm = context.window_manager
|
||||
# return wm.invoke_props_dialog(self, width=400)
|
||||
|
||||
|
||||
class AssetBarOperator(bpy.types.Operator):
|
||||
'''runs search and displays the asset bar at the same time'''
|
||||
bl_idname = "view3d.blenderkit_asset_bar"
|
||||
|
@ -1333,6 +1175,7 @@ class AssetBarOperator(bpy.types.Operator):
|
|||
ui_props.assetbar_on = False
|
||||
|
||||
def modal(self, context, event):
|
||||
|
||||
# This is for case of closing the area or changing type:
|
||||
ui_props = context.scene.blenderkitUI
|
||||
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
|
||||
|
@ -1421,7 +1264,8 @@ class AssetBarOperator(bpy.types.Operator):
|
|||
or ui_props.asset_type == 'SCENE' or ui_props.asset_type == 'HDR':
|
||||
export_data, upload_data = upload.get_upload_data(context=context, asset_type=ui_props.asset_type)
|
||||
if upload_data:
|
||||
ui_props.tooltip = search.generate_tooltip(upload_data)
|
||||
# print(upload_data)
|
||||
ui_props.tooltip = upload_data['displayName']#search.generate_tooltip(upload_data)
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
|
@ -1575,8 +1419,12 @@ class AssetBarOperator(bpy.types.Operator):
|
|||
my = event.mouse_y - r.y
|
||||
|
||||
if event.value == 'PRESS' and mouse_in_asset_bar(mx, my):
|
||||
# bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT')
|
||||
bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
|
||||
context.window.cursor_warp(event.mouse_x - 400, event.mouse_y - 20);
|
||||
|
||||
bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT')
|
||||
context.window.cursor_warp(event.mouse_x, event.mouse_y);
|
||||
|
||||
# bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
if event.type == 'LEFTMOUSE':
|
||||
|
@ -1768,8 +1616,8 @@ class AssetBarOperator(bpy.types.Operator):
|
|||
asset_index=asset_search_index,
|
||||
# replace_resolution=True,
|
||||
invoke_resolution=True,
|
||||
max_resolution = asset_data.get('max_resolution', 0)
|
||||
)
|
||||
max_resolution=asset_data.get('max_resolution', 0)
|
||||
)
|
||||
else:
|
||||
bpy.ops.scene.blenderkit_download( # asset_type=ui_props.asset_type,
|
||||
asset_index=asset_search_index,
|
||||
|
@ -1877,7 +1725,7 @@ class AssetBarOperator(bpy.types.Operator):
|
|||
context.window_manager.modal_handler_add(self)
|
||||
ui_props.assetbar_on = True
|
||||
|
||||
#in an exceptional case these were accessed before drag start.
|
||||
# in an exceptional case these were accessed before drag start.
|
||||
self.drag_start_x = 0
|
||||
self.drag_start_y = 0
|
||||
|
||||
|
@ -1960,7 +1808,7 @@ def find_and_activate_instancers(object):
|
|||
|
||||
|
||||
class AssetDragOperator(bpy.types.Operator):
|
||||
"""Draw a line with the mouse"""
|
||||
"""Drag & drop assets into scene."""
|
||||
bl_idname = "view3d.asset_drag_drop"
|
||||
bl_label = "BlenderKit asset drag drop"
|
||||
|
||||
|
@ -2174,7 +2022,8 @@ class RunAssetBarWithContext(bpy.types.Operator):
|
|||
do_search=self.do_search)
|
||||
|
||||
else:
|
||||
bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=self.keep_running, do_search=self.do_search)
|
||||
bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=self.keep_running,
|
||||
do_search=self.do_search)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
|
|
@ -120,10 +120,14 @@ def draw_image(x, y, width, height, image, transparency, crop=(0, 0, 1, 1)):
|
|||
bgl.glDisable(bgl.GL_TEXTURE_2D)
|
||||
|
||||
|
||||
def draw_text(text, x, y, size, color=(1, 1, 1, 0.5)):
|
||||
font_id = 0
|
||||
def draw_text(text, x, y, size, color=(1, 1, 1, 0.5), ralign = False):
|
||||
font_id = 1
|
||||
# bgl.glColor4f(*color)
|
||||
blf.color(font_id, color[0], color[1], color[2], color[3])
|
||||
blf.position(font_id, x, y, 0)
|
||||
blf.size(font_id, size, 72)
|
||||
if ralign:
|
||||
width,height = blf.dimensions(font_id, text)
|
||||
x-=width
|
||||
blf.position(font_id, x, y, 0)
|
||||
|
||||
blf.draw(font_id, text)
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions, ui, tasks_queue, \
|
||||
autothumb
|
||||
from blenderkit import paths, ratings, ratings_utils, utils, download, categories, icons, search, resolutions, ui, \
|
||||
tasks_queue, \
|
||||
autothumb, upload
|
||||
|
||||
from bpy.types import (
|
||||
Panel
|
||||
|
@ -35,9 +36,7 @@ from bpy.props import (
|
|||
|
||||
import bpy
|
||||
import os
|
||||
import random
|
||||
import logging
|
||||
import blenderkit
|
||||
|
||||
bk_logger = logging.getLogger('blenderkit')
|
||||
|
||||
|
@ -207,7 +206,6 @@ def draw_panel_hdr_search(self, context):
|
|||
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')
|
||||
|
@ -216,6 +214,7 @@ def draw_thumbnail_upload_panel(layout, props):
|
|||
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:
|
||||
|
@ -646,7 +645,8 @@ def draw_panel_material_upload(self, context):
|
|||
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')
|
||||
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')
|
||||
|
@ -1153,12 +1153,13 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
|
|||
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
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:
|
||||
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 wm.get('bkit authors') is not None and author_id is not None:
|
||||
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)
|
||||
|
@ -1172,6 +1173,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
|
|||
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'):
|
||||
|
@ -1244,7 +1246,6 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
|
|||
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:
|
||||
print('context menu')
|
||||
op = col.operator('scene.blenderkit_download', text='Replace asset resolution')
|
||||
|
||||
op.asset_index = ui_props.active_index
|
||||
|
@ -1263,7 +1264,6 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
|
|||
op.model_rotation = (0, 0, 0)
|
||||
op.max_resolution = asset_data.get('max_resolution',
|
||||
0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
|
||||
print('should be drawn!')
|
||||
# print('operator res ', resolution)
|
||||
# op.resolution = resolution
|
||||
|
||||
|
@ -1271,33 +1271,13 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
|
|||
profile = wm.get('bkit profile')
|
||||
if profile is not None:
|
||||
# validation
|
||||
if utils.profile_is_validator():
|
||||
layout.label(text='Validation tools:')
|
||||
layout.operator_context = 'EXEC_DEFAULT'
|
||||
|
||||
if asset_data['verificationStatus'] != 'uploaded':
|
||||
op = layout.operator('object.blenderkit_change_status', text='set Uploaded')
|
||||
op.asset_id = asset_data['id']
|
||||
op.state = 'uploaded'
|
||||
if asset_data['verificationStatus'] != 'validated':
|
||||
op = layout.operator('object.blenderkit_change_status', text='Validate')
|
||||
op.asset_id = asset_data['id']
|
||||
op.state = 'validated'
|
||||
if asset_data['verificationStatus'] != 'on_hold':
|
||||
op = layout.operator('object.blenderkit_change_status', text='Put on Hold')
|
||||
op.asset_id = asset_data['id']
|
||||
op.state = 'on_hold'
|
||||
if asset_data['verificationStatus'] != 'rejected':
|
||||
op = layout.operator('object.blenderkit_change_status', text='Reject')
|
||||
op.asset_id = asset_data['id']
|
||||
op.state = 'rejected'
|
||||
|
||||
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='Fast Edit Metadata')
|
||||
op = layout.operator('wm.blenderkit_fast_metadata', text='Edit Metadata')
|
||||
op.asset_id = asset_data['id']
|
||||
op.asset_type = asset_data['assetType']
|
||||
|
||||
|
@ -1319,7 +1299,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
|
|||
op.state = 'deleted'
|
||||
|
||||
if utils.profile_is_validator():
|
||||
layout.label(text='Admin Tools:')
|
||||
layout.label(text='Dev Tools:')
|
||||
|
||||
op = layout.operator('object.blenderkit_print_asset_debug', text='Print asset debug')
|
||||
op.asset_id = asset_data['id']
|
||||
|
@ -1377,97 +1357,431 @@ class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
|
|||
asset_data = sr[ui_props.active_index]
|
||||
draw_asset_context_menu(self.layout, context, asset_data, from_panel=False)
|
||||
|
||||
# ui_props = context.scene.blenderkitUI
|
||||
#
|
||||
# sr = bpy.context.window_manager['search results']
|
||||
# asset_data = sr[ui_props.active_index]
|
||||
# layout = self.layout
|
||||
# row = layout.row()
|
||||
# split = row.split(factor=0.2)
|
||||
# col = split.column()
|
||||
# op = col.operator('view3d.asset_drag_drop')
|
||||
# op.asset_search_index=ui_props.active_index
|
||||
#
|
||||
# draw_asset_context_menu(col, context, asset_data, from_panel=False)
|
||||
# split = split.split(factor=0.3)
|
||||
# col1 = split.column()
|
||||
# box = col1.box()
|
||||
# utils.label_multiline(box, asset_data['tooltip'])
|
||||
# col2 = split.column()
|
||||
#
|
||||
# pcoll = icons.icon_collections["main"]
|
||||
# my_icon = pcoll['test']
|
||||
# row = col2.row()
|
||||
# row.scale_y = 4
|
||||
# row.template_icon(icon_value=my_icon.icon_id, scale=2.0)
|
||||
# # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0)
|
||||
# box2 = col2.box()
|
||||
#
|
||||
# box2.label(text='and heere goes the rating')
|
||||
# box2.label(text='************')
|
||||
# box2.label(text='dadydadadada')
|
||||
|
||||
def numeric_to_str(s):
|
||||
if s:
|
||||
s = str(round(s))
|
||||
else:
|
||||
s = '-'
|
||||
return s
|
||||
|
||||
|
||||
class AssetPopupCard(bpy.types.Operator):
|
||||
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
|
||||
layout.label(text='')
|
||||
layout.label(text='')
|
||||
|
||||
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
|
||||
layout.label(text='')
|
||||
layout.label(text='')
|
||||
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"
|
||||
# bl_options = {'REGISTER', 'INTERNAL'}
|
||||
bl_options = {'REGISTER', }
|
||||
|
||||
width = 700
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def draw_menu(self, context, layout):
|
||||
col = layout.column()
|
||||
draw_asset_context_menu(col, 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.4)
|
||||
split.alignment = 'RIGHT'
|
||||
split.label(text=left)
|
||||
split = split.split()
|
||||
split.alignment = 'LEFT'
|
||||
# split for questionmark:
|
||||
if url != '':
|
||||
split = split.split(factor=0.7)
|
||||
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
|
||||
self.draw_property(layout, pretext, parameter)
|
||||
|
||||
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']
|
||||
|
||||
layout = layout.column()
|
||||
if len(self.asset_data['description']) > 0:
|
||||
box = layout.box()
|
||||
box.scale_y = 0.8
|
||||
box.label(text='Description')
|
||||
utils.label_multiline(box, self.asset_data['description'], width=width)
|
||||
|
||||
pcoll = icons.icon_collections["main"]
|
||||
|
||||
box = layout.box()
|
||||
box.scale_y = 0.8
|
||||
box.label(text='Properties')
|
||||
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
|
||||
# fs = self.asset_data['files']
|
||||
#
|
||||
# if fs and len(fs) > 2:
|
||||
# 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, 'Resolutions:', resolutions)
|
||||
resolution = utils.get_param(self.asset_data, 'textureResolutionMax')
|
||||
if resolution is not None:
|
||||
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')
|
||||
|
||||
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)
|
||||
# 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)
|
||||
|
||||
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=0.25)
|
||||
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 upload.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):
|
||||
layout.emboss = 'NORMAL'
|
||||
|
||||
box_thumbnail = layout.box()
|
||||
|
||||
box_thumbnail.scale_y = .4
|
||||
|
||||
box_thumbnail.template_icon(icon_value=self.img.preview.icon_id, scale=34.0)
|
||||
|
||||
# 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_left = row.split(factor=split_factor)
|
||||
self.draw_properties(split_left_left, width=int(width * split_factor))
|
||||
|
||||
# right - menu
|
||||
col1 = split_left_left.split()
|
||||
self.draw_menu(context, col1)
|
||||
|
||||
# author
|
||||
self.draw_author_area(context, box, width=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
|
||||
row = layout.row()
|
||||
split = row.split(factor=0.2)
|
||||
col = split.column()
|
||||
op = col.operator('view3d.asset_drag_drop')
|
||||
op.asset_search_index = ui_props.active_index
|
||||
draw_asset_context_menu(col, context, asset_data, from_panel=False)
|
||||
split = split.split(factor=0.5)
|
||||
col1 = split.column()
|
||||
box = col1.box()
|
||||
utils.label_multiline(box, asset_data['tooltip'], width=300)
|
||||
# top draggabe bar with name of the asset
|
||||
top_row = layout.row()
|
||||
top_drag_bar = top_row.box()
|
||||
top_drag_bar.label(text=asset_data['displayName'])
|
||||
|
||||
col2 = split.column()
|
||||
# left side
|
||||
row = layout.row(align=True)
|
||||
|
||||
pcoll = icons.icon_collections["main"]
|
||||
my_icon = pcoll['test']
|
||||
col2.template_icon(icon_value=my_icon.icon_id, scale=20.0)
|
||||
# col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0)
|
||||
box2 = col2.box()
|
||||
split_ratio = 0.5
|
||||
split_left = row.split(factor=0.5)
|
||||
self.draw_thumbnail_box(split_left)
|
||||
|
||||
# draw_ratings(box2, context, asset_data)
|
||||
box2.label(text='Ratings')
|
||||
# print(tp, dir(tp))
|
||||
# if not hasattr(self, 'first_draw'):# try to redraw because of template preview which needs update
|
||||
# for region in context.area.regions:
|
||||
# region.tag_redraw()
|
||||
# self.first_draw = True
|
||||
# right split
|
||||
split_right = split_left.split()
|
||||
self.draw_menu_desc_author(context, split_right, width=int(self.width * split_ratio))
|
||||
|
||||
ratings_box = layout.box()
|
||||
ratings_box.scale_y = 0.7
|
||||
ratings_box.label(text='Rate asset quality:')
|
||||
ratings.draw_ratings_menu(self, context, ratings_box)
|
||||
tip_box = layout.box()
|
||||
tip_box.label(text=self.tip)
|
||||
|
||||
def execute(self, context):
|
||||
print('execute')
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
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)
|
||||
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']
|
||||
return wm.invoke_props_dialog(self, width=700)
|
||||
self.tip = search.get_random_tip()
|
||||
self.tip = self.tip.replace('\n', '')
|
||||
return wm.invoke_popup(self, width=self.width)
|
||||
|
||||
|
||||
class OBJECT_MT_blenderkit_login_menu(bpy.types.Menu):
|
||||
|
|
|
@ -567,6 +567,14 @@ def update_free_full(self, context):
|
|||
" based on our fair share system. " \
|
||||
"Part of subscription is sent to artists based on usage by paying users.")
|
||||
|
||||
def user_is_owner(asset_data=None):
|
||||
'''Checks if the current logged in user is owner of the asset'''
|
||||
profile = bpy.context.window_manager.get('bkit profile')
|
||||
if profile is None:
|
||||
return False
|
||||
if int(asset_data['author']['id']) == int(profile['user']['id']):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_edit_asset(active_index=-1, asset_data=None):
|
||||
if active_index < 0 and not asset_data:
|
||||
|
@ -580,13 +588,13 @@ def can_edit_asset(active_index=-1, asset_data=None):
|
|||
sr = bpy.context.window_manager['search results']
|
||||
asset_data = dict(sr[active_index])
|
||||
# print(profile, asset_data)
|
||||
if asset_data['author']['id'] == profile['user']['id']:
|
||||
if int(asset_data['author']['id']) == int(profile['user']['id']):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class FastMetadata(bpy.types.Operator):
|
||||
"""Fast change of the category of object directly in asset bar."""
|
||||
"""Edit metadata of the asset"""
|
||||
bl_idname = "wm.blenderkit_fast_metadata"
|
||||
bl_label = "Update metadata"
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
|
@ -731,6 +739,7 @@ class FastMetadata(bpy.types.Operator):
|
|||
active_asset = utils.get_active_asset_by_type(asset_type = self.asset_type)
|
||||
asset_data = active_asset.get('asset_data')
|
||||
|
||||
print('can edit asset?', can_edit_asset(asset_data=asset_data))
|
||||
if not can_edit_asset(asset_data=asset_data):
|
||||
return {'CANCELLED'}
|
||||
self.asset_id = asset_data['id']
|
||||
|
@ -1298,7 +1307,7 @@ class AssetDebugPrint(Operator):
|
|||
class AssetVerificationStatusChange(Operator):
|
||||
"""Change verification status"""
|
||||
bl_idname = "object.blenderkit_change_status"
|
||||
bl_description = "Change asset ststus"
|
||||
bl_description = "Change asset status"
|
||||
bl_label = "Change verification status"
|
||||
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
|
||||
|
||||
|
|
|
@ -337,6 +337,12 @@ def get_hidden_texture(name, force_reload=False):
|
|||
return t
|
||||
|
||||
|
||||
def img_to_preview(img):
|
||||
img.preview.image_size = (img.size[0], img.size[1])
|
||||
img.preview.image_pixels_float = img.pixels[:]
|
||||
# img.preview.icon_size = (img.size[0], img.size[1])
|
||||
# img.preview.icon_pixels_float = img.pixels[:]
|
||||
|
||||
def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace='sRGB'):
|
||||
if bdata_name[0] == '.':
|
||||
hidden_name = bdata_name
|
||||
|
@ -355,6 +361,7 @@ def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace='sRGB'):
|
|||
|
||||
if img is None:
|
||||
img = bpy.data.images.load(tpath)
|
||||
img_to_preview(img)
|
||||
img.name = hidden_name
|
||||
else:
|
||||
if img.filepath != tpath:
|
||||
|
@ -363,13 +370,16 @@ def get_hidden_image(tpath, bdata_name, force_reload=False, colorspace='sRGB'):
|
|||
|
||||
img.filepath = tpath
|
||||
img.reload()
|
||||
img_to_preview(img)
|
||||
image_utils.set_colorspace(img, colorspace)
|
||||
|
||||
elif force_reload:
|
||||
if img.packed_file is not None:
|
||||
img.unpack(method='USE_ORIGINAL')
|
||||
img.reload()
|
||||
img_to_preview(img)
|
||||
image_utils.set_colorspace(img, colorspace)
|
||||
|
||||
return img
|
||||
|
||||
|
||||
|
@ -690,6 +700,9 @@ def name_update(props):
|
|||
# Here we actually rename assets datablocks, but don't do that with HDR's and possibly with others
|
||||
asset.name = fname
|
||||
|
||||
def fmt_length(prop):
|
||||
prop = str(round(prop, 2))
|
||||
return prop
|
||||
|
||||
def get_param(asset_data, parameter_name, default = None):
|
||||
if not asset_data.get('parameters'):
|
||||
|
@ -773,7 +786,12 @@ def profile_is_validator():
|
|||
|
||||
|
||||
def guard_from_crash():
|
||||
'''Blender tends to crash when trying to run some functions with the addon going through unregistration process.'''
|
||||
'''
|
||||
Blender tends to crash when trying to run some functions
|
||||
with the addon going through unregistration process.
|
||||
This function is used in these functions (like draw callbacks)
|
||||
so these don't run during unregistration.
|
||||
'''
|
||||
if bpy.context.preferences.addons.get('blenderkit') is None:
|
||||
return False;
|
||||
if bpy.context.preferences.addons['blenderkit'].preferences is None:
|
||||
|
|
|
@ -22,7 +22,7 @@ bl_info = {
|
|||
"name": "Collection Manager",
|
||||
"description": "Manage collections and their objects",
|
||||
"author": "Ryan Inch",
|
||||
"version": (2, 21, 1),
|
||||
"version": (2, 21, 3),
|
||||
"blender": (2, 80, 0),
|
||||
"location": "View3D - Object Mode (Shortcut - M)",
|
||||
"warning": '', # used for warning icon and text in addons panel
|
||||
|
|
|
@ -255,7 +255,7 @@ class QCDSlots():
|
|||
|
||||
x += 1
|
||||
|
||||
if self.length() > 20:
|
||||
if self.length() == 20:
|
||||
break
|
||||
|
||||
else:
|
||||
|
@ -279,7 +279,7 @@ class QCDSlots():
|
|||
|
||||
x += 1
|
||||
|
||||
if self.length() > 20:
|
||||
if self.length() == 20:
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -143,8 +143,13 @@ class CollectionManager(Operator):
|
|||
right_sec = button_row_1.row()
|
||||
right_sec.alignment = 'RIGHT'
|
||||
|
||||
right_sec.menu("VIEW3D_MT_CM_specials_menu")
|
||||
right_sec.popover(panel="COLLECTIONMANAGER_PT_display_options",
|
||||
specials_menu = right_sec.row()
|
||||
specials_menu.alignment = 'RIGHT'
|
||||
specials_menu.menu("VIEW3D_MT_CM_specials_menu")
|
||||
|
||||
display_options = right_sec.row()
|
||||
display_options.alignment = 'RIGHT'
|
||||
display_options.popover(panel="COLLECTIONMANAGER_PT_display_options",
|
||||
text="", icon='FILTER')
|
||||
|
||||
mc_box = layout.box()
|
||||
|
@ -449,12 +454,16 @@ class CollectionManager(Operator):
|
|||
|
||||
if cm.in_phantom_mode:
|
||||
view.enabled = False
|
||||
|
||||
if prefs.enable_qcd:
|
||||
renum_sec.enabled = False
|
||||
|
||||
undo_sec.enabled = False
|
||||
specials_menu.enabled = False
|
||||
|
||||
c_icon.enabled = False
|
||||
row_setcol.enabled = False
|
||||
addcollec_row.enabled = False
|
||||
button_row_2.enabled = False
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
|
|
|
@ -61,10 +61,10 @@ class SceneProperties(PropertyGroup):
|
|||
name="Format",
|
||||
description="Format type to export to",
|
||||
items=(
|
||||
('STL', "STL", ""),
|
||||
('PLY', "PLY", ""),
|
||||
('X3D', "X3D", ""),
|
||||
('OBJ', "OBJ", ""),
|
||||
('PLY', "PLY", ""),
|
||||
('STL', "STL", ""),
|
||||
('X3D', "X3D", ""),
|
||||
),
|
||||
default='STL',
|
||||
)
|
||||
|
@ -78,6 +78,13 @@ class SceneProperties(PropertyGroup):
|
|||
description="Apply scene scale setting on export",
|
||||
default=False,
|
||||
)
|
||||
use_data_layers: BoolProperty(
|
||||
name="Data Layers",
|
||||
description=(
|
||||
"Export normals, UVs, vertex colors and materials for formats that support it "
|
||||
"significantly increasing filesize"
|
||||
),
|
||||
)
|
||||
export_path: StringProperty(
|
||||
name="Export Directory",
|
||||
description="Path to directory where the files are created",
|
||||
|
|
|
@ -81,6 +81,7 @@ def write_mesh(context, report_cb):
|
|||
path_mode = 'COPY' if print_3d.use_export_texture else 'AUTO'
|
||||
export_path = bpy.path.abspath(print_3d.export_path)
|
||||
obj = layer.objects.active
|
||||
export_data_layers = print_3d.use_data_layers
|
||||
|
||||
# Create name 'export_path/blendname-objname'
|
||||
# add the filename component
|
||||
|
@ -129,9 +130,13 @@ def write_mesh(context, report_cb):
|
|||
filepath = bpy.path.ensure_ext(filepath, ".ply")
|
||||
ret = bpy.ops.export_mesh.ply(
|
||||
filepath=filepath,
|
||||
use_ascii=False,
|
||||
use_mesh_modifiers=True,
|
||||
use_selection=True,
|
||||
global_scale=global_scale,
|
||||
use_normals=export_data_layers,
|
||||
use_uv_coords=export_data_layers,
|
||||
use_colors=export_data_layers,
|
||||
)
|
||||
elif export_format == 'X3D':
|
||||
addon_ensure("io_scene_x3d")
|
||||
|
@ -140,8 +145,9 @@ def write_mesh(context, report_cb):
|
|||
filepath=filepath,
|
||||
use_mesh_modifiers=True,
|
||||
use_selection=True,
|
||||
path_mode=path_mode,
|
||||
global_scale=global_scale,
|
||||
path_mode=path_mode,
|
||||
use_normals=export_data_layers,
|
||||
)
|
||||
elif export_format == 'OBJ':
|
||||
addon_ensure("io_scene_obj")
|
||||
|
@ -150,16 +156,18 @@ def write_mesh(context, report_cb):
|
|||
filepath=filepath,
|
||||
use_mesh_modifiers=True,
|
||||
use_selection=True,
|
||||
path_mode=path_mode,
|
||||
global_scale=global_scale,
|
||||
path_mode=path_mode,
|
||||
use_normals=export_data_layers,
|
||||
use_uvs=export_data_layers,
|
||||
use_materials=export_data_layers,
|
||||
)
|
||||
else:
|
||||
assert 0
|
||||
|
||||
# for formats that don't support images
|
||||
if export_format in {'STL', 'PLY'}:
|
||||
if path_mode == 'COPY':
|
||||
image_copy_guess(filepath, context.selected_objects)
|
||||
if path_mode == 'COPY' and export_format in {'STL', 'PLY'}:
|
||||
image_copy_guess(filepath, context.selected_objects)
|
||||
|
||||
if 'FINISHED' in ret:
|
||||
if report_cb is not None:
|
||||
|
|
|
@ -143,10 +143,13 @@ class VIEW3D_PT_print3d_export(View3DPrintPanel, Panel):
|
|||
print_3d = context.scene.print_3d
|
||||
|
||||
layout.prop(print_3d, "export_path", text="")
|
||||
layout.prop(print_3d, "export_format")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(print_3d, "use_apply_scale")
|
||||
col.prop(print_3d, "use_export_texture")
|
||||
sub = col.column()
|
||||
sub.active = print_3d.export_format != "STL"
|
||||
sub.prop(print_3d, "use_data_layers")
|
||||
|
||||
layout.prop(print_3d, "export_format")
|
||||
layout.operator("mesh.print3d_export", text="Export", icon='EXPORT')
|
||||
|
|
|
@ -682,4 +682,4 @@ def set_bone_widget_transform(obj, bone_name, transform_bone, use_size=True, sca
|
|||
bone.custom_shape_transform = None
|
||||
|
||||
bone.use_custom_shape_bone_size = use_size
|
||||
bone.custom_shape_scale = scale
|
||||
bone.custom_shape_scale_xyz = (scale, scale, scale)
|
||||
|
|
|
@ -23,7 +23,7 @@ import math
|
|||
import inspect
|
||||
import functools
|
||||
|
||||
from mathutils import Matrix
|
||||
from mathutils import Matrix, Vector, Euler
|
||||
|
||||
from .errors import MetarigError
|
||||
from .collections import ensure_widget_collection
|
||||
|
@ -42,7 +42,10 @@ def obj_to_bone(obj, rig, bone_name, bone_transform_name=None):
|
|||
raise MetarigError("obj_to_bone(): does not work while in edit mode")
|
||||
|
||||
bone = rig.pose.bones[bone_name]
|
||||
scale = bone.custom_shape_scale
|
||||
|
||||
loc = bone.custom_shape_translation
|
||||
rot = bone.custom_shape_rotation_euler
|
||||
scale = Vector(bone.custom_shape_scale_xyz)
|
||||
|
||||
if bone.use_custom_shape_bone_size:
|
||||
scale *= bone.length
|
||||
|
@ -52,8 +55,10 @@ def obj_to_bone(obj, rig, bone_name, bone_transform_name=None):
|
|||
elif bone.custom_shape_transform:
|
||||
bone = bone.custom_shape_transform
|
||||
|
||||
shape_mat = Matrix.Translation(loc) @ (Euler(rot).to_matrix() @ Matrix.Diagonal(scale)).to_4x4()
|
||||
|
||||
obj.rotation_mode = 'XYZ'
|
||||
obj.matrix_basis = rig.matrix_world @ bone.bone.matrix_local @ Matrix.Scale(scale, 4)
|
||||
obj.matrix_basis = rig.matrix_world @ bone.bone.matrix_local @ shape_mat
|
||||
|
||||
|
||||
def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, widget_force_new=False):
|
||||
|
|
|
@ -177,7 +177,9 @@ def pVisScaExec(bone, active, context):
|
|||
def pDrwExec(bone, active, context):
|
||||
bone.custom_shape = active.custom_shape
|
||||
bone.use_custom_shape_bone_size = active.use_custom_shape_bone_size
|
||||
bone.custom_shape_scale = active.custom_shape_scale
|
||||
bone.custom_shape_translation = active.custom_shape_translation
|
||||
bone.custom_shape_rotation_euler = active.custom_shape_rotation_euler
|
||||
bone.custom_shape_scale_xyz = active.custom_shape_scale_xyz
|
||||
bone.bone.show_wire = active.bone.show_wire
|
||||
|
||||
|
||||
|
|