Merge branch 'blender-v2.92-release'
[blender-addons.git] / blenderkit / ui_panels.py
blob4948b65165bc924fbe2adf1f72ae4234a123c766
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
20 from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions, ui
22 from bpy.types import (
23 Panel
25 from bpy.props import (
26 IntProperty,
27 FloatProperty,
28 FloatVectorProperty,
29 StringProperty,
30 EnumProperty,
31 BoolProperty,
32 PointerProperty,
35 import bpy
36 import os
37 import random
38 import blenderkit
41 # this was moved to separate interface:
43 def draw_ratings(layout, context, asset):
44 # layout.operator("wm.url_open", text="Read rating instructions", icon='QUESTION').url = 'https://support.google.com/?hl=en'
45 # the following shouldn't happen at all in an optimal case,
46 # this function should run only when asset was already checked to be existing
47 if asset == None:
48 return;
50 col = layout.column()
51 bkit_ratings = asset.bkit_ratings
53 # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
55 row = col.row()
56 row.prop(bkit_ratings, 'rating_quality_ui', expand=True, icon_only=True, emboss=False)
57 if bkit_ratings.rating_quality > 0:
58 col.separator()
59 col.prop(bkit_ratings, 'rating_work_hours')
60 # w = context.region.width
62 # layout.label(text='problems')
63 # layout.prop(bkit_ratings, 'rating_problems', text='')
64 # layout.label(text='compliments')
65 # layout.prop(bkit_ratings, 'rating_compliments', text='')
67 # row = layout.row()
68 # op = row.operator("object.blenderkit_rating_upload", text="Send rating", icon='URL')
69 # return op
70 # re-enable layout if included in longer panel
73 def draw_not_logged_in(source, message='Please Login/Signup to use this feature'):
74 title = "You aren't logged in"
76 def draw_message(source, context):
77 layout = source.layout
78 utils.label_multiline(layout, text=message)
79 draw_login_buttons(layout)
81 bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
84 def draw_upload_common(layout, props, asset_type, context):
85 op = layout.operator("wm.url_open", text=f"Read {asset_type.lower()} upload instructions",
86 icon='QUESTION')
87 if asset_type == 'MODEL':
88 op.url = paths.BLENDERKIT_MODEL_UPLOAD_INSTRUCTIONS_URL
89 if asset_type == 'MATERIAL':
90 op.url = paths.BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL
91 if asset_type == 'BRUSH':
92 op.url = paths.BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL
93 if asset_type == 'SCENE':
94 op.url = paths.BLENDERKIT_SCENE_UPLOAD_INSTRUCTIONS_URL
95 if asset_type == 'HDR':
96 op.url = paths.BLENDERKIT_HDR_UPLOAD_INSTRUCTIONS_URL
98 row = layout.row(align=True)
99 if props.upload_state != '':
100 utils.label_multiline(layout, text=props.upload_state, width=context.region.width)
101 if props.uploading:
102 op = layout.operator('object.kill_bg_process', text="", icon='CANCEL')
103 op.process_source = asset_type
104 op.process_type = 'UPLOAD'
105 layout = layout.column()
106 layout.enabled = False
107 # if props.upload_state.find('Error') > -1:
108 # layout.label(text = props.upload_state)
110 if props.asset_base_id == '':
111 optext = 'Upload %s' % asset_type.lower()
112 op = layout.operator("object.blenderkit_upload", text=optext, icon='EXPORT')
113 op.asset_type = asset_type
114 op.reupload = False
115 #make sure everything gets uploaded.
116 op.main_file = True
117 op.metadata = True
118 op.thumbnail = True
120 if props.asset_base_id != '':
121 op = layout.operator("object.blenderkit_upload", text='Reupload asset', icon='EXPORT')
122 op.asset_type = asset_type
123 op.reupload = True
125 op = layout.operator("object.blenderkit_upload", text='Upload as new asset', icon='EXPORT')
126 op.asset_type = asset_type
127 op.reupload = False
129 # layout.label(text = 'asset id, overwrite only for reuploading')
130 layout.label(text='asset has a version online.')
131 # row = layout.row()
132 # row.enabled = False
133 # row.prop(props, 'asset_base_id', icon='FILE_TICK')
134 # row = layout.row()
135 # row.enabled = False
136 # row.prop(props, 'id', icon='FILE_TICK')
137 layout.prop(props, 'category')
138 if props.category != 'NONE' and props.subcategory != 'NONE':
139 layout.prop(props, 'subcategory')
140 if props.subcategory != 'NONE' and props.subcategory1 != 'NONE':
141 layout.prop(props, 'subcategory1')
143 layout.prop(props, 'is_private', expand=True)
144 if props.is_private == 'PUBLIC':
145 layout.prop(props, 'license')
148 def poll_local_panels():
149 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
150 return user_preferences.panel_behaviour == 'BOTH' or user_preferences.panel_behaviour == 'LOCAL'
153 def prop_needed(layout, props, name, value, is_not_filled=''):
154 row = layout.row()
155 if value == is_not_filled:
156 # row.label(text='', icon = 'ERROR')
157 icon = 'ERROR'
158 row.alert = True
159 row.prop(props, name) # , icon=icon)
160 row.alert = False
161 else:
162 # row.label(text='', icon = 'FILE_TICK')
163 icon = None
164 row.prop(props, name)
166 def draw_panel_hdr_upload(self, context):
167 layout = self.layout
168 ui_props = bpy.context.scene.blenderkitUI
170 # layout.prop_search(ui_props, "hdr_upload_image", bpy.data, "images")
171 layout.prop(ui_props, "hdr_upload_image")
173 hdr = utils.get_active_HDR()
176 if hdr is not None:
177 props = hdr.blenderkit
179 layout = self.layout
181 draw_upload_common(layout, props, 'HDR', context)
183 layout.prop(props, 'name')
184 layout.prop(props, 'description')
185 layout.prop(props, 'tags')
187 def draw_panel_hdr_search(self, context):
188 s = context.scene
189 props = s.blenderkit_HDR
191 layout = self.layout
192 row = layout.row()
193 row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
194 draw_assetbar_show_hide(row, props)
195 layout.prop(props, "own_only")
197 utils.label_multiline(layout, text=props.report)
199 def draw_panel_model_upload(self, context):
200 ob = bpy.context.active_object
201 while ob.parent is not None:
202 ob = ob.parent
203 props = ob.blenderkit
205 layout = self.layout
207 draw_upload_common(layout, props, 'MODEL', context)
209 prop_needed(layout, props, 'name', props.name)
211 col = layout.column()
212 if props.is_generating_thumbnail:
213 col.enabled = False
214 prop_needed(col, props, 'thumbnail', props.has_thumbnail, False)
215 if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'):
216 col.operator("object.blenderkit_generate_thumbnail", text='Generate thumbnail', icon='IMAGE')
218 # row = layout.row(align=True)
219 if props.is_generating_thumbnail:
220 row = layout.row(align=True)
221 row.label(text=props.thumbnail_generating_state)
222 op = row.operator('object.kill_bg_process', text="", icon='CANCEL')
223 op.process_source = 'MODEL'
224 op.process_type = 'THUMBNAILER'
225 elif props.thumbnail_generating_state != '':
226 utils.label_multiline(layout, text=props.thumbnail_generating_state)
228 layout.prop(props, 'description')
229 layout.prop(props, 'tags')
230 # prop_needed(layout, props, 'style', props.style)
231 # prop_needed(layout, props, 'production_level', props.production_level)
232 layout.prop(props, 'style')
233 layout.prop(props, 'production_level')
235 layout.prop(props, 'condition')
236 layout.prop(props, 'is_free')
237 layout.prop(props, 'pbr')
238 layout.label(text='design props:')
239 layout.prop(props, 'manufacturer')
240 layout.prop(props, 'designer')
241 layout.prop(props, 'design_collection')
242 layout.prop(props, 'design_variant')
243 layout.prop(props, 'use_design_year')
244 if props.use_design_year:
245 layout.prop(props, 'design_year')
247 row = layout.row()
248 row.prop(props, 'work_hours')
250 layout.prop(props, 'adult')
253 def draw_panel_scene_upload(self, context):
254 s = bpy.context.scene
255 props = s.blenderkit
257 layout = self.layout
258 # if bpy.app.debug_value != -1:
259 # layout.label(text='Scene upload not Implemented')
260 # return
261 draw_upload_common(layout, props, 'SCENE', context)
263 # layout = layout.column()
265 # row = layout.row()
267 # if props.dimensions[0] + props.dimensions[1] == 0 and props.face_count == 0:
268 # icon = 'ERROR'
269 # layout.operator("object.blenderkit_auto_tags", text='Auto fill tags', icon=icon)
270 # else:
271 # layout.operator("object.blenderkit_auto_tags", text='Auto fill tags')
273 prop_needed(layout, props, 'name', props.name)
275 col = layout.column()
276 # if props.is_generating_thumbnail:
277 # col.enabled = False
278 prop_needed(col, props, 'thumbnail', props.has_thumbnail, False)
279 # if bpy.context.scene.render.engine == 'CYCLES':
280 # col.operator("object.blenderkit_generate_thumbnail", text='Generate thumbnail', icon='IMAGE_COL')
282 # row = layout.row(align=True)
283 # if props.is_generating_thumbnail:
284 # row = layout.row(align=True)
285 # row.label(text = props.thumbnail_generating_state)
286 # op = row.operator('object.kill_bg_process', text="", icon='CANCEL')
287 # op.process_source = 'MODEL'
288 # op.process_type = 'THUMBNAILER'
289 # elif props.thumbnail_generating_state != '':
290 # utils.label_multiline(layout, text = props.thumbnail_generating_state)
292 layout.prop(props, 'is_free')
293 layout.prop(props, 'description')
294 layout.prop(props, 'tags')
295 layout.prop(props, 'style')
296 layout.prop(props, 'production_level')
297 layout.prop(props, 'use_design_year')
298 if props.use_design_year:
299 layout.prop(props, 'design_year')
300 layout.prop(props, 'condition')
301 row = layout.row()
302 row.prop(props, 'work_hours')
303 layout.prop(props, 'adult')
306 def draw_assetbar_show_hide(layout, props):
307 s = bpy.context.scene
308 ui_props = s.blenderkitUI
310 if ui_props.assetbar_on:
311 icon = 'HIDE_OFF'
312 ttip = 'Click to Hide Asset Bar'
313 else:
314 icon = 'HIDE_ON'
315 ttip = 'Click to Show Asset Bar'
317 preferences = bpy.context.preferences.addons['blenderkit'].preferences
318 if preferences.experimental_features:
319 op = layout.operator('view3d.blenderkit_asset_bar_widget', text = '', icon = icon)
320 op.keep_running = False
321 op.do_search = False
322 op.tooltip = ttip
323 else:
324 op = layout.operator('view3d.blenderkit_asset_bar', text='', icon=icon)
325 op.keep_running = False
326 op.do_search = False
328 op.tooltip = ttip
331 def draw_panel_model_search(self, context):
332 s = context.scene
334 props = s.blenderkit_models
335 layout = self.layout
337 row = layout.row()
338 row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
339 draw_assetbar_show_hide(row, props)
341 icon = 'NONE'
342 if props.report == 'You need Full plan to get this item.':
343 icon = 'ERROR'
344 utils.label_multiline(layout, text=props.report, icon=icon)
345 if props.report == 'You need Full plan to get this item.':
346 layout.operator("wm.url_open", text="Get Full plan", icon='URL').url = paths.BLENDERKIT_PLANS
348 layout.prop(props, "search_style")
349 layout.prop(props, "own_only")
350 layout.prop(props, "free_only")
352 # if props.search_style == 'OTHER':
353 # layout.prop(props, "search_style_other")
354 # layout.prop(props, "search_engine")
355 # col = layout.column()
356 # layout.prop(props, 'append_link', expand=True, icon_only=False)
357 # layout.prop(props, 'import_as', expand=True, icon_only=False)
359 # draw_panel_categories(self, context)
362 def draw_panel_scene_search(self, context):
363 s = context.scene
364 props = s.blenderkit_scene
365 layout = self.layout
366 # layout.label(text = "common search properties:")
367 row = layout.row()
368 row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
369 draw_assetbar_show_hide(row, props)
370 layout.prop(props, "own_only")
371 utils.label_multiline(layout, text=props.report)
373 # layout.prop(props, "search_style")
374 # if props.search_style == 'OTHER':
375 # layout.prop(props, "search_style_other")
376 # layout.prop(props, "search_engine")
377 layout.separator()
378 # draw_panel_categories(self, context)
381 class VIEW3D_PT_blenderkit_model_properties(Panel):
382 bl_category = "BlenderKit"
383 bl_idname = "VIEW3D_PT_blenderkit_model_properties"
384 bl_space_type = 'VIEW_3D'
385 bl_region_type = 'UI'
386 bl_label = "Selected Model"
387 bl_context = "objectmode"
389 @classmethod
390 def poll(cls, context):
391 p = bpy.context.view_layer.objects.active is not None
392 return p
394 def draw(self, context):
395 # draw asset properties here
396 layout = self.layout
398 o = utils.get_active_model()
399 # o = bpy.context.active_object
400 if o.get('asset_data') is None:
401 utils.label_multiline(layout,
402 text='To upload this asset to BlenderKit, go to the Find and Upload Assets panel.')
403 layout.prop(o, 'name')
405 if o.get('asset_data') is not None:
406 ad = o['asset_data']
407 layout.label(text=str(ad['name']))
408 if o.instance_type == 'COLLECTION' and o.instance_collection is not None:
409 layout.operator('object.blenderkit_bring_to_scene', text='Bring to scene')
410 layout.label(text='Ratings:')
411 draw_panel_model_rating(self, context)
413 layout.label(text='Asset tools:')
414 draw_asset_context_menu(self.layout, context, ad, from_panel=True)
415 # if 'rig' in ad['tags']:
416 # # layout.label(text = 'can make proxy')
417 # layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
418 # fast upload, blocked by now
419 # else:
420 # op = layout.operator("object.blenderkit_upload", text='Store as private', icon='EXPORT')
421 # op.asset_type = 'MODEL'
422 # op.fast = True
423 # fun override project, not finished
424 # layout.operator('object.blenderkit_color_corrector')
427 class NODE_PT_blenderkit_material_properties(Panel):
428 bl_category = "BlenderKit"
429 bl_idname = "NODE_PT_blenderkit_material_properties"
430 bl_space_type = 'NODE_EDITOR'
431 bl_region_type = 'UI'
432 bl_label = "Selected Material"
433 bl_context = "objectmode"
435 @classmethod
436 def poll(cls, context):
437 p = bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None
438 return p
440 def draw(self, context):
441 # draw asset properties here
442 layout = self.layout
444 m = bpy.context.active_object.active_material
445 # o = bpy.context.active_object
446 if m.get('asset_data') is None and m.blenderkit.id == '':
447 utils.label_multiline(layout,
448 text='To upload this asset to BlenderKit, go to the Find and Upload Assets panel.')
449 layout.prop(m, 'name')
451 if m.get('asset_data') is not None:
452 ad = m['asset_data']
453 layout.label(text=str(ad['name']))
454 layout.label(text='Ratings:')
455 draw_panel_material_ratings(self, context)
457 layout.label(text='Asset tools:')
458 draw_asset_context_menu(self.layout, context, ad, from_panel=True)
459 # if 'rig' in ad['tags']:
460 # # layout.label(text = 'can make proxy')
461 # layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
462 # fast upload, blocked by now
463 # else:
464 # op = layout.operator("object.blenderkit_upload", text='Store as private', icon='EXPORT')
465 # op.asset_type = 'MODEL'
466 # op.fast = True
467 # fun override project, not finished
468 # layout.operator('object.blenderkit_color_corrector')
471 def draw_rating_asset(self, context, asset):
472 layout = self.layout
473 col = layout.box()
474 # split = layout.split(factor=0.5)
475 # col1 = split.column()
476 # col2 = split.column()
477 # print('%s_search' % asset['asset_data']['assetType'])
478 directory = paths.get_temp_dir('%s_search' % asset['asset_data']['assetType'])
479 tpath = os.path.join(directory, asset['asset_data']['thumbnail_small'])
480 for image in bpy.data.images:
481 if image.filepath == tpath:
482 # split = row.split(factor=1.0, align=False)
483 col.template_icon(icon_value=image.preview.icon_id, scale=6.0)
484 break;
485 # layout.label(text = '', icon_value=image.preview.icon_id, scale = 10)
486 col.label(text=asset.name)
487 draw_ratings(col, context, asset=asset)
490 class VIEW3D_PT_blenderkit_ratings(Panel):
491 bl_category = "BlenderKit"
492 bl_idname = "VIEW3D_PT_blenderkit_ratings"
493 bl_space_type = 'VIEW_3D'
494 bl_region_type = 'UI'
495 bl_label = "Please rate"
496 bl_context = "objectmode"
498 @classmethod
499 def poll(cls, context):
501 p = bpy.context.view_layer.objects.active is not None
502 return p
504 def draw(self, context):
505 # TODO make a list of assets inside asset appending code, to happen only when assets are added to the scene.
506 # draw asset properties here
507 layout = self.layout
508 assets = ratings.get_assets_for_rating()
509 if len(assets) > 0:
510 utils.label_multiline(layout, text='Please help BlenderKit community by rating these assets:')
512 for a in assets:
513 if a.bkit_ratings.rating_work_hours==0:
514 draw_rating_asset(self, context, asset=a)
517 def draw_login_progress(layout):
518 layout.label(text='Login through browser')
519 layout.label(text='in progress.')
520 layout.operator("wm.blenderkit_login_cancel", text="Cancel", icon='CANCEL')
523 class VIEW3D_PT_blenderkit_profile(Panel):
524 bl_category = "BlenderKit"
525 bl_idname = "VIEW3D_PT_blenderkit_profile"
526 bl_space_type = 'VIEW_3D'
527 bl_region_type = 'UI'
528 bl_label = "BlenderKit Profile"
530 @classmethod
531 def poll(cls, context):
533 return True
535 def draw(self, context):
536 # draw asset properties here
537 layout = self.layout
538 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
540 if user_preferences.login_attempt:
541 draw_login_progress(layout)
542 return
544 if user_preferences.api_key != '':
545 me = bpy.context.window_manager.get('bkit profile')
546 if me is not None:
547 me = me['user']
548 # user name
549 layout.label(text='Me: %s %s' % (me['firstName'], me['lastName']))
550 # layout.label(text='Email: %s' % (me['email']))
552 # plan information
554 if me.get('currentPlanName') is not None:
555 pn = me['currentPlanName']
556 pcoll = icons.icon_collections["main"]
557 if pn == 'Free':
558 my_icon = pcoll['free']
559 else:
560 my_icon = pcoll['full']
562 row = layout.row()
563 row.label(text='My plan:')
564 row.label(text='%s plan' % pn, icon_value=my_icon.icon_id)
565 if pn == 'Free':
566 layout.operator("wm.url_open", text="Change plan",
567 icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_PLANS
569 # storage statistics
570 # if me.get('sumAssetFilesSize') is not None: # TODO remove this when production server has these too.
571 # layout.label(text='My public assets: %i MiB' % (me['sumAssetFilesSize']))
572 # if me.get('sumPrivateAssetFilesSize') is not None:
573 # layout.label(text='My private assets: %i MiB' % (me['sumPrivateAssetFilesSize']))
574 if me.get('remainingPrivateQuota') is not None:
575 layout.label(text='My free storage: %i MiB' % (me['remainingPrivateQuota']))
577 layout.operator("wm.url_open", text="See my uploads",
578 icon='URL').url = paths.get_bkit_url() + paths.BLENDERKIT_USER_ASSETS
581 class VIEW3D_PT_blenderkit_login(Panel):
582 bl_category = "BlenderKit"
583 bl_idname = "VIEW3D_PT_blenderkit_login"
584 bl_space_type = 'VIEW_3D'
585 bl_region_type = 'UI'
586 bl_label = "BlenderKit Login"
588 @classmethod
589 def poll(cls, context):
590 return True
592 def draw(self, context):
593 layout = self.layout
594 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
596 if user_preferences.login_attempt:
597 draw_login_progress(layout)
598 return
600 if user_preferences.enable_oauth:
601 draw_login_buttons(layout)
604 def draw_panel_model_rating(self, context):
605 # o = bpy.context.active_object
606 o = utils.get_active_model()
607 # print('ratings active',o)
608 draw_ratings(self.layout, context, asset=o) # , props)
609 # op.asset_type = 'MODEL'
612 def draw_panel_material_upload(self, context):
613 o = bpy.context.active_object
614 mat = bpy.context.active_object.active_material
616 props = mat.blenderkit
617 layout = self.layout
619 draw_upload_common(layout, props, 'MATERIAL', context)
621 prop_needed(layout, props, 'name', props.name)
622 layout.prop(props, 'description')
623 layout.prop(props, 'style')
624 # if props.style == 'OTHER':
625 # layout.prop(props, 'style_other')
626 # layout.prop(props, 'engine')
627 # if props.engine == 'OTHER':
628 # layout.prop(props, 'engine_other')
629 layout.prop(props, 'tags')
630 # layout.prop(props,'shaders')#TODO autofill on upload
631 # row = layout.row()
632 layout.prop(props, 'is_free')
634 layout.prop(props, 'pbr')
635 layout.prop(props, 'uv')
636 layout.prop(props, 'animated')
637 layout.prop(props, 'texture_size_meters')
639 # THUMBNAIL
640 row = layout.row()
641 if props.is_generating_thumbnail:
642 row.enabled = False
643 prop_needed(row, props, 'thumbnail', props.has_thumbnail, False)
645 if props.is_generating_thumbnail:
646 row = layout.row(align=True)
647 row.label(text=props.thumbnail_generating_state, icon='RENDER_STILL')
648 op = row.operator('object.kill_bg_process', text="", icon='CANCEL')
649 op.process_source = 'MATERIAL'
650 op.process_type = 'THUMBNAILER'
651 elif props.thumbnail_generating_state != '':
652 utils.label_multiline(layout, text=props.thumbnail_generating_state)
654 if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'):
655 layout.operator("object.blenderkit_material_thumbnail", text='Render thumbnail with Cycles', icon='EXPORT')
657 # tname = "." + bpy.context.active_object.active_material.name + "_thumbnail"
658 # if props.has_thumbnail and bpy.data.textures.get(tname) is not None:
659 # row = layout.row()
660 # # row.scale_y = 1.5
661 # row.template_preview(bpy.data.textures[tname], preview_id='test')
664 def draw_panel_material_search(self, context):
665 wm = context.scene
666 props = wm.blenderkit_mat
668 layout = self.layout
669 row = layout.row()
670 row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
671 draw_assetbar_show_hide(row, props)
672 layout.prop(props, "own_only")
673 utils.label_multiline(layout, text=props.report)
675 # layout.prop(props, 'search_style')F
676 # if props.search_style == 'OTHER':
677 # layout.prop(props, 'search_style_other')
678 # layout.prop(props, 'search_engine')
679 # if props.search_engine == 'OTHER':
680 # layout.prop(props, 'search_engine_other')
682 # draw_panel_categories(self, context)
685 def draw_panel_material_ratings(self, context):
686 asset = bpy.context.active_object.active_material
687 draw_ratings(self.layout, context, asset) # , props)
688 # op.asset_type = 'MATERIAL'
691 def draw_panel_brush_upload(self, context):
692 brush = utils.get_active_brush()
693 if brush is not None:
694 props = brush.blenderkit
696 layout = self.layout
698 draw_upload_common(layout, props, 'BRUSH', context)
700 layout.prop(props, 'name')
701 layout.prop(props, 'description')
702 layout.prop(props, 'tags')
705 def draw_panel_brush_search(self, context):
706 s = context.scene
707 props = s.blenderkit_brush
709 layout = self.layout
710 row = layout.row()
711 row.prop(props, "search_keywords", text="", icon='VIEWZOOM')
712 draw_assetbar_show_hide(row, props)
713 layout.prop(props, "own_only")
715 utils.label_multiline(layout, text=props.report)
716 # draw_panel_categories(self, context)
719 def draw_panel_brush_ratings(self, context):
720 # props = utils.get_brush_props(context)
721 brush = utils.get_active_brush()
722 draw_ratings(self.layout, context, asset=brush) # , props)
724 # op.asset_type = 'BRUSH'
727 def draw_login_buttons(layout, invoke=False):
728 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
730 if user_preferences.login_attempt:
731 draw_login_progress(layout)
732 else:
733 if invoke:
734 layout.operator_context = 'INVOKE_DEFAULT'
735 else:
736 layout.operator_context = 'EXEC_DEFAULT'
737 if user_preferences.api_key == '':
738 layout.operator("wm.blenderkit_login", text="Login",
739 icon='URL').signup = False
740 layout.operator("wm.blenderkit_login", text="Sign up",
741 icon='URL').signup = True
743 else:
744 layout.operator("wm.blenderkit_login", text="Login as someone else",
745 icon='URL').signup = False
746 layout.operator("wm.blenderkit_logout", text="Logout",
747 icon='URL')
750 class VIEW3D_PT_blenderkit_advanced_model_search(Panel):
751 bl_category = "BlenderKit"
752 bl_idname = "VIEW3D_PT_blenderkit_advanced_model_search"
753 bl_parent_id = "VIEW3D_PT_blenderkit_unified"
754 bl_space_type = 'VIEW_3D'
755 bl_region_type = 'UI'
756 bl_label = "Search filters"
757 bl_options = {'DEFAULT_CLOSED'}
759 @classmethod
760 def poll(cls, context):
761 s = context.scene
762 ui_props = s.blenderkitUI
763 return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MODEL'
765 def draw(self, context):
766 s = context.scene
768 props = s.blenderkit_models
769 layout = self.layout
770 layout.separator()
772 # layout.label(text = "common searches keywords:")
773 # layout.prop(props, "search_global_keywords", text = "")
774 # layout.prop(props, "search_modifier_keywords")
775 # if props.search_engine == 'OTHER':
776 # layout.prop(props, "search_engine_keyword")
778 # AGE
779 layout.prop(props, "search_condition", text='Condition') # , text ='condition of object new/old e.t.c.')
781 # DESIGN YEAR
782 layout.prop(props, "search_design_year", text='designed in ( min - max )')
783 if props.search_design_year:
784 row = layout.row(align=True)
785 row.prop(props, "search_design_year_min", text='min')
786 row.prop(props, "search_design_year_max", text='max')
788 # POLYCOUNT
789 layout.prop(props, "search_polycount", text='Poly count in ( min - max )')
790 if props.search_polycount:
791 row = layout.row(align=True)
792 row.prop(props, "search_polycount_min", text='min')
793 row.prop(props, "search_polycount_max", text='max')
795 # TEXTURE RESOLUTION
796 layout.prop(props, "search_texture_resolution", text='texture resolution ( min - max )')
797 if props.search_texture_resolution:
798 row = layout.row(align=True)
799 row.prop(props, "search_texture_resolution_min", text='min')
800 row.prop(props, "search_texture_resolution_max", text='max')
802 # FILE SIZE
803 layout.prop(props, "search_file_size", text='File size ( min - max MB)')
804 if props.search_file_size:
805 row = layout.row(align=True)
806 row.prop(props, "search_file_size_min", text='min')
807 row.prop(props, "search_file_size_max", text='max')
809 # layout.prop(props, "search_procedural", expand=True)
810 # ADULT
811 # layout.prop(props, "search_adult") # , text ='condition of object new/old e.t.c.')
814 class VIEW3D_PT_blenderkit_advanced_material_search(Panel):
815 bl_category = "BlenderKit"
816 bl_idname = "VIEW3D_PT_blenderkit_advanced_material_search"
817 bl_parent_id = "VIEW3D_PT_blenderkit_unified"
818 bl_space_type = 'VIEW_3D'
819 bl_region_type = 'UI'
820 bl_label = "Search filters"
821 bl_options = {'DEFAULT_CLOSED'}
823 @classmethod
824 def poll(cls, context):
825 s = context.scene
826 ui_props = s.blenderkitUI
827 return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MATERIAL'
829 def draw(self, context):
830 s = context.scene
832 props = s.blenderkit_mat
833 layout = self.layout
834 layout.separator()
836 layout.label(text='texture types')
837 col = layout.column()
838 col.prop(props, "search_procedural", expand=True)
840 if props.search_procedural == 'TEXTURE_BASED':
841 # TEXTURE RESOLUTION
842 layout.prop(props, "search_texture_resolution", text='texture resolution ( min - max )')
843 if props.search_texture_resolution:
844 row = layout.row(align=True)
845 row.prop(props, "search_texture_resolution_min", text='min')
846 row.prop(props, "search_texture_resolution_max", text='max')
848 # FILE SIZE
849 layout.prop(props, "search_file_size", text='File size ( min - max MB)')
850 if props.search_file_size:
851 row = layout.row(align=True)
852 row.prop(props, "search_file_size_min", text='min')
853 row.prop(props, "search_file_size_max", text='max')
856 class VIEW3D_PT_blenderkit_categories(Panel):
857 bl_category = "BlenderKit"
858 bl_idname = "VIEW3D_PT_blenderkit_categories"
859 bl_space_type = 'VIEW_3D'
860 bl_region_type = 'UI'
861 bl_label = "Categories"
862 bl_parent_id = "VIEW3D_PT_blenderkit_unified"
863 bl_options = {'DEFAULT_CLOSED'}
865 @classmethod
866 def poll(cls, context):
867 s = context.scene
868 ui_props = s.blenderkitUI
869 mode = True
870 if ui_props.asset_type == 'BRUSH' and not (context.sculpt_object or context.image_paint_object):
871 mode = False
872 return ui_props.down_up == 'SEARCH' and mode
874 def draw(self, context):
875 draw_panel_categories(self, context)
878 class VIEW3D_PT_blenderkit_import_settings(Panel):
879 bl_category = "BlenderKit"
880 bl_idname = "VIEW3D_PT_blenderkit_import_settings"
881 bl_space_type = 'VIEW_3D'
882 bl_region_type = 'UI'
883 bl_label = "Import settings"
884 bl_parent_id = "VIEW3D_PT_blenderkit_unified"
885 bl_options = {'DEFAULT_CLOSED'}
887 @classmethod
888 def poll(cls, context):
889 s = context.scene
890 ui_props = s.blenderkitUI
891 return ui_props.down_up == 'SEARCH' and ui_props.asset_type in ['MATERIAL', 'MODEL', 'HDR']
893 def draw(self, context):
894 layout = self.layout
896 s = context.scene
897 ui_props = s.blenderkitUI
899 if ui_props.asset_type == 'MODEL':
900 # noinspection PyCallByClass
901 props = s.blenderkit_models
902 layout.prop(props, 'randomize_rotation')
903 if props.randomize_rotation:
904 layout.prop(props, 'randomize_rotation_amount')
905 layout.prop(props, 'perpendicular_snap')
906 # if props.perpendicular_snap:
907 # layout.prop(props,'perpendicular_snap_threshold')
909 layout.label(text='Import method:')
910 row = layout.row()
911 row.prop(props, 'append_method', expand=True, icon_only=False)
913 if ui_props.asset_type == 'MATERIAL':
914 props = s.blenderkit_mat
915 layout.prop(props, 'automap')
916 layout.label(text='Import method:')
917 row = layout.row()
919 row.prop(props, 'append_method', expand=True, icon_only=False)
920 if ui_props.asset_type == 'HDR':
921 props = s.blenderkit_HDR
923 layout.prop(props, 'resolution')
924 # layout.prop(props, 'unpack_files')
927 class VIEW3D_PT_blenderkit_unified(Panel):
928 bl_category = "BlenderKit"
929 bl_idname = "VIEW3D_PT_blenderkit_unified"
930 bl_space_type = 'VIEW_3D'
931 bl_region_type = 'UI'
932 bl_label = "Find and Upload Assets"
934 @classmethod
935 def poll(cls, context):
936 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
937 return user_preferences.panel_behaviour == 'BOTH' or user_preferences.panel_behaviour == 'UNIFIED'
939 def draw(self, context):
940 s = context.scene
941 ui_props = s.blenderkitUI
942 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
943 wm = bpy.context.window_manager
944 layout = self.layout
945 # layout.prop_tabs_enum(ui_props, "asset_type", icon_only = True)
947 row = layout.row()
948 # row.scale_x = 1.6
949 # row.scale_y = 1.6
951 row.prop(ui_props, 'down_up', expand=True, icon_only=False)
952 # row.label(text='')
953 # row = row.split().row()
954 # layout.alert = True
955 # layout.alignment = 'CENTER'
956 row = layout.row(align=True)
957 row.scale_x = 1.6
958 row.scale_y = 1.6
959 # split = row.split(factor=.
960 col = layout.column()
961 col.prop(ui_props, 'asset_type', expand=True, icon_only=False)
962 # row = layout.column(align = False)
963 # layout.prop(ui_props, 'asset_type', expand=False, text='')
965 w = context.region.width
966 if user_preferences.login_attempt:
967 draw_login_progress(layout)
968 return
970 if len(user_preferences.api_key) < 20 and user_preferences.asset_counter > 20:
971 if user_preferences.enable_oauth:
972 draw_login_buttons(layout)
973 else:
974 op = layout.operator("wm.url_open", text="Get your API Key",
975 icon='QUESTION')
976 op.url = paths.BLENDERKIT_SIGNUP_URL
977 layout.label(text='Paste your API Key:')
978 layout.prop(user_preferences, 'api_key', text='')
979 layout.separator()
980 # if bpy.data.filepath == '':
981 # layout.alert = True
982 # utils.label_multiline(layout, text="It's better to save your file first.", width=w)
983 # layout.alert = False
984 # layout.separator()
986 if ui_props.down_up == 'SEARCH':
987 if utils.profile_is_validator():
988 search_props = utils.get_search_props()
989 layout.prop(search_props, 'search_verification_status')
990 layout.prop(search_props, "unrated_only")
992 if ui_props.asset_type == 'MODEL':
993 # noinspection PyCallByClass
994 draw_panel_model_search(self, context)
995 if ui_props.asset_type == 'SCENE':
996 # noinspection PyCallByClass
997 draw_panel_scene_search(self, context)
998 if ui_props.asset_type == 'HDR':
999 # noinspection PyCallByClass
1000 draw_panel_hdr_search(self, context)
1001 elif ui_props.asset_type == 'MATERIAL':
1002 draw_panel_material_search(self, context)
1003 elif ui_props.asset_type == 'BRUSH':
1004 if context.sculpt_object or context.image_paint_object:
1005 # noinspection PyCallByClass
1006 draw_panel_brush_search(self, context)
1007 else:
1008 utils.label_multiline(layout, text='Switch to paint or sculpt mode.', width=context.region.width)
1009 return
1012 elif ui_props.down_up == 'UPLOAD':
1013 if not ui_props.assetbar_on:
1014 text = 'Show asset preview - ;'
1015 else:
1016 text = 'Hide asset preview - ;'
1017 op = layout.operator('view3d.blenderkit_asset_bar', text=text, icon='EXPORT')
1018 op.keep_running = False
1019 op.do_search = False
1020 op.tooltip = 'Show/Hide asset preview'
1022 e = s.render.engine
1023 if e not in ('CYCLES', 'BLENDER_EEVEE'):
1024 rtext = 'Only Cycles and EEVEE render engines are currently supported. ' \
1025 'Please use Cycles for all assets you upload to BlenderKit.'
1026 utils.label_multiline(layout, rtext, icon='ERROR', width=w)
1027 return;
1029 if ui_props.asset_type == 'MODEL':
1030 # utils.label_multiline(layout, "Uploaded models won't be available in b2.79", icon='ERROR')
1031 if bpy.context.view_layer.objects.active is not None:
1032 draw_panel_model_upload(self, context)
1033 else:
1034 layout.label(text='selet object to upload')
1035 elif ui_props.asset_type == 'SCENE':
1036 draw_panel_scene_upload(self, context)
1037 elif ui_props.asset_type == 'HDR':
1038 draw_panel_hdr_upload(self, context)
1040 elif ui_props.asset_type == 'MATERIAL':
1041 # utils.label_multiline(layout, "Uploaded materials won't be available in b2.79", icon='ERROR')
1043 if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None:
1044 draw_panel_material_upload(self, context)
1045 else:
1046 utils.label_multiline(layout, text='select object with material to upload materials', width=w)
1048 elif ui_props.asset_type == 'BRUSH':
1049 if context.sculpt_object or context.image_paint_object:
1050 draw_panel_brush_upload(self, context)
1051 else:
1052 layout.label(text='Switch to paint or sculpt mode.')
1054 elif ui_props.down_up == 'RATING': # the poll functions didn't work here, don't know why.
1056 if ui_props.asset_type == 'MODEL':
1057 # TODO improve poll here to parenting structures
1058 if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.get(
1059 'asset_data') != None:
1060 ad = bpy.context.active_object.get('asset_data')
1061 layout.label(text=ad['name'])
1062 draw_panel_model_rating(self, context)
1063 if ui_props.asset_type == 'MATERIAL':
1064 if bpy.context.view_layer.objects.active is not None and \
1065 bpy.context.active_object.active_material is not None and \
1066 bpy.context.active_object.active_material.blenderkit.asset_base_id != '':
1067 layout.label(text=bpy.context.active_object.active_material.blenderkit.name + ' :')
1068 # noinspection PyCallByClass
1069 draw_panel_material_ratings(self, context)
1070 if ui_props.asset_type == 'BRUSH':
1071 if context.sculpt_object or context.image_paint_object:
1072 props = utils.get_brush_props(context)
1073 if props.asset_base_id != '':
1074 layout.label(text=props.name + ' :')
1075 # noinspection PyCallByClass
1076 draw_panel_brush_ratings(self, context)
1077 if ui_props.asset_type == 'TEXTURE':
1078 layout.label(text='not yet implemented')
1081 class BlenderKitWelcomeOperator(bpy.types.Operator):
1082 """Login online on BlenderKit webpage"""
1084 bl_idname = "wm.blenderkit_welcome"
1085 bl_label = "Welcome to BlenderKit!"
1086 bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
1088 step: IntProperty(
1089 name="step",
1090 description="Tutorial Step",
1091 default=0,
1092 options={'SKIP_SAVE'}
1095 @classmethod
1096 def poll(cls, context):
1097 return True
1099 def draw(self, context):
1100 layout = self.layout
1101 if self.step == 0:
1102 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
1104 message = "BlenderKit connects from Blender to an online, " \
1105 "community built shared library of models, " \
1106 "materials, and brushes. " \
1107 "Use addon preferences to set up where files will be saved in the Global directory setting."
1109 utils.label_multiline(layout, text=message, width=300)
1110 utils.label_multiline(layout, text="\n Let's start by searching for some cool materials?", width=300)
1111 else:
1112 message = "Operator Tutorial called with invalid step"
1114 def execute(self, context):
1115 if self.step == 0:
1116 # move mouse:
1117 # bpy.context.window_manager.windows[0].cursor_warp(1000, 1000)
1118 # show n-key sidebar (spaces[index] has to be found for view3d too:
1119 # bpy.context.window_manager.windows[0].screen.areas[5].spaces[0].show_region_ui = False
1120 print('running search no')
1121 ui_props = bpy.context.scene.blenderkitUI
1122 ui_props.asset_type = 'MATERIAL'
1123 bpy.context.scene.blenderkit_mat.search_keywords = 'ice'
1124 # search.search()
1125 return {'FINISHED'}
1127 def invoke(self, context, event):
1128 wm = bpy.context.window_manager
1129 return wm.invoke_props_dialog(self)
1132 def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
1133 ui_props = context.scene.blenderkitUI
1135 author_id = str(asset_data['author'].get('id'))
1136 wm = bpy.context.window_manager
1138 layout.operator_context = 'INVOKE_DEFAULT'
1140 op = layout.operator('wm.blenderkit_menu_rating_upload', text='Rate')
1141 op.asset_name = asset_data['name']
1142 op.asset_id = asset_data['id']
1143 op.asset_type = asset_data['assetType']
1145 if wm.get('bkit authors') is not None and author_id is not None:
1146 a = bpy.context.window_manager['bkit authors'].get(author_id)
1147 if a is not None:
1148 # utils.p('author:', a)
1149 op = layout.operator('wm.url_open', text="Open Author's Website")
1150 if a.get('aboutMeUrl') is not None:
1151 op.url = a['aboutMeUrl']
1152 else:
1153 op.url = paths.get_author_gallery_url(a['id'])
1154 op = layout.operator('view3d.blenderkit_search', text="Show Assets By Author")
1155 op.keywords = ''
1156 op.author_id = author_id
1158 op = layout.operator('view3d.blenderkit_search', text='Search Similar')
1159 # build search string from description and tags:
1160 op.keywords = asset_data['name']
1161 if asset_data.get('description'):
1162 op.keywords += ' ' + asset_data.get('description')
1163 op.keywords += ' '.join(asset_data.get('tags'))
1165 if asset_data.get('canDownload') != 0:
1166 if len(bpy.context.selected_objects) > 0 and ui_props.asset_type == 'MODEL':
1167 aob = bpy.context.active_object
1168 if aob is None:
1169 aob = bpy.context.selected_objects[0]
1170 op = layout.operator('scene.blenderkit_download', text='Replace Active Models')
1172 # this checks if the menu got called from right-click in assetbar(then index is 0 - x) or
1173 # from a panel(then replacement happens from the active model)
1174 if from_panel:
1175 # called from addon panel
1176 op.asset_base_id = asset_data['assetBaseId']
1177 else:
1178 op.asset_index = ui_props.active_index
1180 # op.asset_type = ui_props.asset_type
1181 op.model_location = aob.location
1182 op.model_rotation = aob.rotation_euler
1183 op.target_object = aob.name
1184 op.material_target_slot = aob.active_material_index
1185 op.replace = True
1186 op.replace_resolution = False
1188 # resolution replacement operator
1189 # if asset_data['downloaded'] == 100: # only show for downloaded/used assets
1190 # if ui_props.asset_type in ('MODEL', 'MATERIAL'):
1191 # layout.menu(OBJECT_MT_blenderkit_resolution_menu.bl_idname)
1194 if ui_props.asset_type in ('MODEL', 'MATERIAL', 'HDR') and \
1195 utils.get_param(asset_data, 'textureResolutionMax') is not None and \
1196 utils.get_param(asset_data, 'textureResolutionMax') > 512:
1198 s = bpy.context.scene
1200 col = layout.column()
1201 col.operator_context = 'INVOKE_DEFAULT'
1203 if from_panel:
1204 # Called from addon panel
1206 if asset_data.get('resolution'):
1207 op = col.operator('scene.blenderkit_download', text='Replace asset resolution')
1208 op.asset_base_id = asset_data['assetBaseId']
1209 if asset_data['assetType'] == 'MODEL':
1210 o = utils.get_active_model()
1211 op.model_location = o.location
1212 op.model_rotation = o.rotation_euler
1213 op.target_object = o.name
1214 op.material_target_slot = o.active_material_index
1216 elif asset_data['assetType'] == 'MATERIAL':
1217 aob = bpy.context.active_object
1218 op.model_location = aob.location
1219 op.model_rotation = aob.rotation_euler
1220 op.target_object = aob.name
1221 op.material_target_slot = aob.active_material_index
1222 op.replace_resolution = True
1223 op.replace = False
1225 op.invoke_resolution = True
1226 op.max_resolution = asset_data.get('max_resolution',
1227 0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
1229 elif asset_data['assetBaseId'] in s['assets used'].keys():
1230 # called from asset bar:
1231 print('context menu')
1232 op = col.operator('scene.blenderkit_download', text='Replace asset resolution')
1234 op.asset_index = ui_props.active_index
1235 # op.asset_type = ui_props.asset_type
1236 op.replace_resolution = True
1237 op.replace = False
1238 op.invoke_resolution = True
1239 o = utils.get_active_model()
1240 if o and o.get('asset_data'):
1241 if o['asset_data']['assetBaseId'] == bpy.context.scene['search results'][ui_props.active_index]:
1242 op.model_location = o.location
1243 op.model_rotation = o.rotation_euler
1244 else:
1245 op.model_location = (0, 0, 0)
1246 op.model_rotation = (0, 0, 0)
1247 op.max_resolution = asset_data.get('max_resolution',
1248 0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
1249 print('should be drawn!')
1250 # print('operator res ', resolution)
1251 # op.resolution = resolution
1253 wm = bpy.context.window_manager
1254 profile = wm.get('bkit profile')
1255 if profile is not None:
1256 # validation
1257 if utils.profile_is_validator():
1258 layout.label(text='Validation tools:')
1259 layout.operator_context = 'EXEC_DEFAULT'
1261 if asset_data['verificationStatus'] != 'uploaded':
1262 op = layout.operator('object.blenderkit_change_status', text='set Uploaded')
1263 op.asset_id = asset_data['id']
1264 op.state = 'uploaded'
1265 if asset_data['verificationStatus'] != 'validated':
1266 op = layout.operator('object.blenderkit_change_status', text='Validate')
1267 op.asset_id = asset_data['id']
1268 op.state = 'validated'
1269 if asset_data['verificationStatus'] != 'on_hold':
1270 op = layout.operator('object.blenderkit_change_status', text='Put on Hold')
1271 op.asset_id = asset_data['id']
1272 op.state = 'on_hold'
1273 if asset_data['verificationStatus'] != 'rejected':
1274 op = layout.operator('object.blenderkit_change_status', text='Reject')
1275 op.asset_id = asset_data['id']
1276 op.state = 'rejected'
1278 if author_id == str(profile['user']['id']) or utils.profile_is_validator():
1279 layout.label(text='Management tools:')
1281 row = layout.row()
1282 row.operator_context = 'INVOKE_DEFAULT'
1283 op = layout.operator('wm.blenderkit_fast_metadata', text='Fast Edit Metadata')
1284 op.asset_id = asset_data['id']
1286 if author_id == str(profile['user']['id']):
1287 row = layout.row()
1288 row.operator_context = 'INVOKE_DEFAULT'
1289 op = row.operator('object.blenderkit_change_status', text='Delete')
1290 op.asset_id = asset_data['id']
1291 op.state = 'deleted'
1293 if utils.profile_is_validator():
1294 layout.label(text='Admin Tools:')
1297 op = layout.operator('object.blenderkit_print_asset_debug', text='Print asset debug')
1298 op.asset_id = asset_data['id']
1304 # def draw_asset_resolution_replace(self, context, resolution):
1305 # layout = self.layout
1306 # ui_props = bpy.context.scene.blenderkitUI
1308 # op = layout.operator('scene.blenderkit_download', text=resolution)
1309 # if ui_props.active_index == -3:
1310 # # This happens if the command is called from addon panel
1311 # o = utils.get_active_model()
1312 # op.asset_base_id = o['asset_data']['assetBaseId']
1314 # else:
1315 # op.asset_index = ui_props.active_index
1317 # op.asset_type = ui_props.asset_type
1318 # if len(bpy.context.selected_objects) > 0: # and ui_props.asset_type == 'MODEL':
1319 # aob = bpy.context.active_object
1320 # op.model_location = aob.location
1321 # op.model_rotation = aob.rotation_euler
1322 # op.target_object = aob.name
1323 # op.material_target_slot = aob.active_material_index
1324 # op.replace_resolution = True
1325 # print('operator res ', resolution)
1326 # op.resolution = resolution
1329 # class OBJECT_MT_blenderkit_resolution_menu(bpy.types.Menu):
1330 # bl_label = "Replace Asset Resolution"
1331 # bl_idname = "OBJECT_MT_blenderkit_resolution_menu"
1333 # def draw(self, context):
1334 # ui_props = context.scene.blenderkitUI
1336 # # sr = bpy.context.scene['search results']
1338 # # sr = bpy.context.scene['search results']
1339 # # asset_data = sr[ui_props.active_index]
1341 # for k in resolutions.resolution_props_to_server.keys():
1342 # draw_asset_resolution_replace(self, context, k)
1345 class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
1346 bl_label = "Asset options:"
1347 bl_idname = "OBJECT_MT_blenderkit_asset_menu"
1349 def draw(self, context):
1350 ui_props = context.scene.blenderkitUI
1352 sr = bpy.context.scene['search results']
1353 asset_data = sr[ui_props.active_index]
1354 draw_asset_context_menu(self.layout, context, asset_data, from_panel=False)
1356 # ui_props = context.scene.blenderkitUI
1358 # sr = bpy.context.scene['search results']
1359 # asset_data = sr[ui_props.active_index]
1360 # layout = self.layout
1361 # row = layout.row()
1362 # split = row.split(factor=0.2)
1363 # col = split.column()
1364 # op = col.operator('view3d.asset_drag_drop')
1365 # op.asset_search_index=ui_props.active_index
1367 # draw_asset_context_menu(col, context, asset_data, from_panel=False)
1368 # split = split.split(factor=0.3)
1369 # col1 = split.column()
1370 # box = col1.box()
1371 # utils.label_multiline(box, asset_data['tooltip'])
1372 # col2 = split.column()
1374 # pcoll = icons.icon_collections["main"]
1375 # my_icon = pcoll['test']
1376 # row = col2.row()
1377 # row.scale_y = 4
1378 # row.template_icon(icon_value=my_icon.icon_id, scale=2.0)
1379 # # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0)
1380 # box2 = col2.box()
1382 # box2.label(text='and heere goes the rating')
1383 # box2.label(text='************')
1384 # box2.label(text='dadydadadada')
1386 class AssetPopupCard(bpy.types.Operator):
1387 """Generate Cycles thumbnail for model assets"""
1388 bl_idname = "wm.blenderkit_asset_popup"
1389 bl_label = "BlenderKit asset popup"
1390 # bl_options = {'REGISTER', 'INTERNAL'}
1391 bl_options = {'REGISTER',}
1393 @classmethod
1394 def poll(cls, context):
1395 return True
1397 def draw(self, context):
1398 ui_props = context.scene.blenderkitUI
1400 sr = bpy.context.scene['search results']
1401 asset_data = sr[ui_props.active_index]
1402 layout = self.layout
1403 row = layout.row()
1404 split = row.split(factor=0.2)
1405 col = split.column()
1406 op = col.operator('view3d.asset_drag_drop')
1407 op.asset_search_index = ui_props.active_index
1408 draw_asset_context_menu(col, context, asset_data, from_panel=False)
1409 split = split.split(factor=0.5)
1410 col1 = split.column()
1411 box = col1.box()
1412 utils.label_multiline(box,asset_data['tooltip'], width = 300)
1414 col2 = split.column()
1417 pcoll = icons.icon_collections["main"]
1418 my_icon = pcoll['test']
1419 col2.template_icon(icon_value=my_icon.icon_id, scale=20.0)
1420 # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0)
1421 box2 = col2.box()
1423 # draw_ratings(box2, context, asset_data)
1424 box2.label(text = 'Ratings')
1425 # print(tp, dir(tp))
1426 # if not hasattr(self, 'first_draw'):# try to redraw because of template preview which needs update
1427 # for region in context.area.regions:
1428 # region.tag_redraw()
1429 # self.first_draw = True
1431 def execute(self, context):
1432 print('execute')
1433 return {'FINISHED'}
1435 def invoke(self, context, event):
1436 wm = context.window_manager
1437 ui_props = context.scene.blenderkitUI
1438 ui_props.draw_tooltip = False
1439 sr = bpy.context.scene['search results']
1440 asset_data = sr[ui_props.active_index]
1441 self.img = ui.get_large_thumbnail_image(asset_data)
1442 # self.tex = utils.get_hidden_texture(self.img)
1443 # self.tex.update_tag()
1445 bl_label = asset_data['name']
1446 return wm.invoke_props_dialog(self, width = 700)
1448 class OBJECT_MT_blenderkit_login_menu(bpy.types.Menu):
1449 bl_label = "BlenderKit login/signup:"
1450 bl_idname = "OBJECT_MT_blenderkit_login_menu"
1452 def draw(self, context):
1453 layout = self.layout
1455 # utils.label_multiline(layout, text=message)
1456 draw_login_buttons(layout)
1459 class SetCategoryOperator(bpy.types.Operator):
1460 """Visit subcategory"""
1461 bl_idname = "view3d.blenderkit_set_category"
1462 bl_label = "BlenderKit Set Active Category"
1463 bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
1465 category: bpy.props.StringProperty(
1466 name="Category",
1467 description="set this category active",
1468 default="")
1470 asset_type: bpy.props.StringProperty(
1471 name="Asset Type",
1472 description="asset type",
1473 default="")
1475 @classmethod
1476 def poll(cls, context):
1477 return True
1479 def execute(self, context):
1480 acat = bpy.context.window_manager['active_category'][self.asset_type]
1481 if self.category == '':
1482 acat.remove(acat[-1])
1483 else:
1484 acat.append(self.category)
1485 # we have to write back to wm. Thought this should happen with original list.
1486 bpy.context.window_manager['active_category'][self.asset_type] = acat
1487 return {'FINISHED'}
1490 class UrlPopupDialog(bpy.types.Operator):
1491 """Generate Cycles thumbnail for model assets"""
1492 bl_idname = "wm.blenderkit_url_dialog"
1493 bl_label = "BlenderKit message:"
1494 bl_options = {'REGISTER', 'INTERNAL'}
1496 url: bpy.props.StringProperty(
1497 name="Url",
1498 description="url",
1499 default="")
1501 link_text: bpy.props.StringProperty(
1502 name="Url",
1503 description="url",
1504 default="Go to website")
1506 message: bpy.props.StringProperty(
1507 name="Text",
1508 description="text",
1509 default="")
1511 # @classmethod
1512 # def poll(cls, context):
1513 # return bpy.context.view_layer.objects.active is not None
1515 def draw(self, context):
1516 layout = self.layout
1517 utils.label_multiline(layout, text=self.message)
1519 layout.active_default = True
1520 op = layout.operator("wm.url_open", text=self.link_text, icon='QUESTION')
1521 op.url = self.url
1523 def execute(self, context):
1524 # start_thumbnailer(self, context)
1525 return {'FINISHED'}
1527 def invoke(self, context, event):
1528 wm = context.window_manager
1530 return wm.invoke_props_dialog(self)
1533 class LoginPopupDialog(bpy.types.Operator):
1534 """Popup a dialog which enables the user to log in after being logged out automatically."""
1535 bl_idname = "wm.blenderkit_login_dialog"
1536 bl_label = "BlenderKit login"
1537 bl_options = {'REGISTER', 'INTERNAL'}
1539 message: bpy.props.StringProperty(
1540 name="Message",
1541 description="",
1542 default="Your were logged out from BlenderKit. Please login again. ")
1544 # @classmethod
1545 # def poll(cls, context):
1546 # return bpy.context.view_layer.objects.active is not None
1548 def draw(self, context):
1549 layout = self.layout
1550 utils.label_multiline(layout, text=self.message)
1552 layout.active_default = True
1553 op = layout.operator
1554 op = layout.operator("wm.url_open", text=self.link_text, icon='QUESTION')
1555 op.url = self.url
1557 def execute(self, context):
1558 # start_thumbnailer(self, context)
1559 return {'FINISHED'}
1561 def invoke(self, context, event):
1562 wm = context.window_manager
1564 return wm.invoke_props_dialog(self)
1567 def draw_panel_categories(self, context):
1568 s = context.scene
1569 ui_props = s.blenderkitUI
1570 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
1571 layout = self.layout
1572 # row = layout.row()
1573 # row.prop(ui_props, 'asset_type', expand=True, icon_only=True)
1574 layout.separator()
1576 layout.label(text='Categories')
1577 wm = bpy.context.window_manager
1578 if wm.get('bkit_categories') == None:
1579 return
1580 col = layout.column(align=True)
1581 if wm.get('active_category') is not None:
1582 acat = wm['active_category'][ui_props.asset_type]
1583 if len(acat) > 1:
1584 # we are in subcategory, so draw the parent button
1585 op = col.operator('view3d.blenderkit_set_category', text='...', icon='FILE_PARENT')
1586 op.asset_type = ui_props.asset_type
1587 op.category = ''
1588 cats = categories.get_category(wm['bkit_categories'], cat_path=acat)
1589 # draw freebies only in models parent category
1590 # if ui_props.asset_type == 'MODEL' and len(acat) == 1:
1591 # op = col.operator('view3d.blenderkit_asset_bar', text='freebies')
1592 # op.free_only = True
1594 for c in cats['children']:
1595 if c['assetCount'] > 0:
1596 row = col.row(align=True)
1597 if len(c['children']) > 0 and c['assetCount'] > 15:
1598 row = row.split(factor=.8, align=True)
1599 # row = split.split()
1600 ctext = '%s (%i)' % (c['name'], c['assetCount'])
1602 preferences = bpy.context.preferences.addons['blenderkit'].preferences
1603 if preferences.experimental_features:
1604 op = row.operator('view3d.blenderkit_asset_bar_widget', text=ctext)
1605 else:
1606 op = row.operator('view3d.blenderkit_asset_bar', text=ctext)
1607 op.do_search = True
1608 op.keep_running = True
1609 op.category = c['slug']
1610 # TODO enable subcategories, now not working due to some bug on server probably
1611 if len(c['children']) > 0 and c['assetCount'] > 15:
1612 # row = row.split()
1613 op = row.operator('view3d.blenderkit_set_category', text='>>')
1614 op.asset_type = ui_props.asset_type
1615 op.category = c['slug']
1616 # for c1 in c['children']:
1617 # if c1['assetCount']>0:
1618 # row = col.row()
1619 # split = row.split(percentage=.2)
1620 # row = split.split()
1621 # row = split.split()
1622 # ctext = '%s (%i)' % (c1['name'], c1['assetCount'])
1623 # op = row.operator('view3d.blenderkit_search', text=ctext)
1624 # op.category = c1['slug']
1627 class VIEW3D_PT_blenderkit_downloads(Panel):
1628 bl_category = "BlenderKit"
1629 bl_idname = "VIEW3D_PT_blenderkit_downloads"
1630 bl_space_type = 'VIEW_3D'
1631 bl_region_type = 'UI'
1632 bl_label = "Downloads"
1634 @classmethod
1635 def poll(cls, context):
1636 return len(download.download_threads) > 0
1638 def draw(self, context):
1639 layout = self.layout
1640 for i, threaddata in enumerate(download.download_threads):
1641 tcom = threaddata[2]
1642 asset_data = threaddata[1]
1643 row = layout.row()
1644 row.label(text=asset_data['name'])
1645 row.label(text=str(int(tcom.progress)) + ' %')
1646 op = row.operator('scene.blenderkit_download_kill', text='', icon='CANCEL')
1647 op.thread_index = i
1648 if tcom.passargs.get('retry_counter', 0) > 0:
1649 row = layout.row()
1650 row.label(text='failed. retrying ... ', icon='ERROR')
1651 row.label(text=str(tcom.passargs["retry_counter"]))
1653 layout.separator()
1656 def header_search_draw(self, context):
1657 '''Top bar menu in 3D view'''
1659 if not utils.guard_from_crash():
1660 return;
1662 preferences = bpy.context.preferences.addons['blenderkit'].preferences
1663 if preferences.search_in_header:
1664 layout = self.layout
1665 s = bpy.context.scene
1666 ui_props = s.blenderkitUI
1667 if ui_props.asset_type == 'MODEL':
1668 props = s.blenderkit_models
1669 if ui_props.asset_type == 'MATERIAL':
1670 props = s.blenderkit_mat
1671 if ui_props.asset_type == 'BRUSH':
1672 props = s.blenderkit_brush
1673 if ui_props.asset_type == 'HDR':
1674 props = s.blenderkit_HDR
1675 if ui_props.asset_type == 'SCENE':
1676 props = s.blenderkit_scene
1678 # the center snap menu is in edit and object mode if tool settings are off.
1679 if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'):
1680 layout.separator_spacer()
1681 layout.prop(ui_props, "asset_type", expand = True, icon_only = True, text='', icon='URL')
1682 layout.prop(props, "search_keywords", text="", icon='VIEWZOOM')
1683 draw_assetbar_show_hide(layout, props)
1686 # We can store multiple preview collections here,
1687 # however in this example we only store "main"
1688 preview_collections = {}
1690 classes = (
1691 SetCategoryOperator,
1692 VIEW3D_PT_blenderkit_profile,
1693 VIEW3D_PT_blenderkit_login,
1694 VIEW3D_PT_blenderkit_unified,
1695 VIEW3D_PT_blenderkit_advanced_model_search,
1696 VIEW3D_PT_blenderkit_advanced_material_search,
1697 VIEW3D_PT_blenderkit_categories,
1698 VIEW3D_PT_blenderkit_import_settings,
1699 VIEW3D_PT_blenderkit_model_properties,
1700 NODE_PT_blenderkit_material_properties,
1701 # VIEW3D_PT_blenderkit_ratings,
1702 VIEW3D_PT_blenderkit_downloads,
1703 # OBJECT_MT_blenderkit_resolution_menu,
1704 OBJECT_MT_blenderkit_asset_menu,
1705 OBJECT_MT_blenderkit_login_menu,
1706 AssetPopupCard,
1707 UrlPopupDialog,
1708 BlenderKitWelcomeOperator,
1712 def register_ui_panels():
1713 for c in classes:
1714 bpy.utils.register_class(c)
1715 bpy.types.VIEW3D_MT_editor_menus.append(header_search_draw)
1718 def unregister_ui_panels():
1719 bpy.types.VIEW3D_MT_editor_menus.remove(header_search_draw)
1720 for c in classes:
1721 # print('unregister', c)
1722 bpy.utils.unregister_class(c)