BlederKit: fix snapping with particles

Now the snap also ignores particles.
Also fix a case if user didn't fill a name in profile, now it draws at least users email.
This commit is contained in:
Vilém Duha 2021-01-28 13:59:29 +01:00
parent 4b6ee68c00
commit 2afbdc806e
8 changed files with 122 additions and 44 deletions

View File

@ -582,16 +582,11 @@ def name_update(self, context):
def update_free(self, context):
if self.is_free == False:
self.is_free = True
title = "All BlenderKit materials are free"
message = "Any material uploaded to BlenderKit is free." \
ui_panels.ui_message(title = "All BlenderKit materials are free",
message = "Any material uploaded to BlenderKit is free." \
" However, it can still earn money for the author," \
" based on our fair share system. " \
"Part of subscription is sent to artists based on usage by paying users."
def draw_message(self, context):
utils.label_multiline(self.layout, text=message, icon='NONE', width=-1)
bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
"Part of subscription is sent to artists based on usage by paying users.")
class BlenderKitCommonUploadProps(object):
@ -1517,15 +1512,11 @@ def fix_subdir(self, context):
if self.project_subdir != pp:
self.project_subdir = pp
title = "Fixed to relative path"
message = "This path should be always realative.\n" \
" It's a directory BlenderKit creates where your .blend is \n " \
"and uses it for storing assets."
ui_panels.ui_message(title = "Fixed to relative path",
message = "This path should be always realative.\n" \
" It's a directory BlenderKit creates where your .blend is \n " \
"and uses it for storing assets.")
def draw_message(self, context):
utils.label_multiline(self.layout, text=message, icon='NONE', width=400)
bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
class BlenderKitAddonPreferences(AddonPreferences):

View File

@ -516,6 +516,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
sro = bpy.context.window_manager.get('search results orig')
if sro is not None and sro.get('next') is not None:
blenderkit.search.search(get_next=True)
def update_images(self):
sr = bpy.context.window_manager['search results']

View File

@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
from blenderkit import paths, utils, bg_blender
from blenderkit import paths, utils, bg_blender, ui_panels
import tempfile, os, subprocess, json, sys
@ -262,13 +262,10 @@ class GenerateThumbnailOperator(bpy.types.Operator):
def invoke(self, context, event):
wm = context.window_manager
if bpy.data.filepath == '':
title = "Can't render thumbnail"
message = "please save your file first"
ui_panels.ui_message(
title = "Can't render thumbnail",
message = "please save your file first")
def draw_message(self, context):
self.layout.label(text=message)
bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
return {'FINISHED'}
return wm.invoke_props_dialog(self)

View File

@ -601,7 +601,7 @@ def assets_db_path():
def get_assets_search():
bpy.app.debug_value = 2
# bpy.app.debug_value = 2
results = []
preferences = bpy.context.preferences.addons['blenderkit'].preferences

View File

@ -237,7 +237,7 @@ def parse_result(r):
# except:
# utils.p('asset with no files-size')
asset_type = r['assetType']
if len(r['files']) > 0:
if len(r['files']) > 0:#TODO remove this condition so all assets are parsed.
r['available_resolutions'] = []
allthumbs = []
durl, tname, small_tname = '', '', ''
@ -403,7 +403,6 @@ def timer_update():
result_field = []
ok, error = check_errors(rdata)
if ok:
bpy.ops.object.run_assetbar_fix_context()
for r in rdata['results']:
asset_data = parse_result(r)
@ -1278,7 +1277,6 @@ def add_search_process(query, params, orig_result):
old_thread[0].stop()
# TODO CARE HERE FOR ALSO KILLING THE Thumbnail THREADS.?
# AT LEAST NOW SEARCH DONE FIRST WON'T REWRITE AN OLDER ONE
tempdir = paths.get_temp_dir('%s_search' % query['asset_type'])
thread = Searcher(query, params, orig_result)
thread.start()
@ -1343,7 +1341,6 @@ def search(category='', get_next=False, author_id=''):
''' initialize searching'''
global search_start_time
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
search_start_time = time.time()
# mt('start')
scene = bpy.context.scene
@ -1418,7 +1415,7 @@ def search(category='', get_next=False, author_id=''):
# if free_only:
# query['keywords'] += '+is_free:true'
orig_results = scene.get(f'bkit {ui_props.asset_type.lower()} search orig', {})
orig_results = bpy.context.window_manager.get(f'bkit {ui_props.asset_type.lower()} search orig', {})
if orig_results != {}:
# ensure it's a copy in dict for what we are passing to thread:
orig_results = orig_results.to_dict()

View File

@ -883,6 +883,19 @@ def draw_callback_3d(self, context):
if ui.draw_snapped_bounds:
draw_bbox(ui.snapped_location, ui.snapped_rotation, ui.snapped_bbox_min, ui.snapped_bbox_max)
def object_in_particle_collection(o):
'''checks if an object is in a particle system as instance, to not snap to it and not to try to attach material.'''
for p in bpy.data.particles:
if p.render_type =='COLLECTION':
if p.instance_collection:
for o1 in p.instance_collection.objects:
if o1 == o:
return True
if p.render_type =='COLLECTION':
if p.instance_object == o:
return True
return False
def deep_ray_cast(depsgraph, ray_origin, vec):
# this allows to ignore some objects, like objects with bounding box draw style or particle objects
@ -890,19 +903,22 @@ def deep_ray_cast(depsgraph, ray_origin, vec):
# while object is None or object.draw
has_hit, snapped_location, snapped_normal, face_index, object, matrix = bpy.context.scene.ray_cast(
depsgraph, ray_origin, vec)
empty_set = False, Vector((0, 0, 0)), Vector((0, 0, 1)), None, None, None
if not object:
return False, Vector((0, 0, 0)), Vector((0, 0, 1)), None, None, None
return empty_set
try_object = object
while try_object and try_object.display_type == 'BOUNDS':
while try_object and (try_object.display_type == 'BOUNDS' or object_in_particle_collection(try_object)):
ray_origin = snapped_location + vec.normalized() * 0.0003
try_has_hit, try_snapped_location, try_snapped_normal, try_face_index, try_object, try_matrix = bpy.context.scene.ray_cast(
depsgraph, ray_origin, vec)
if try_has_hit:
#this way only good hits are returned, otherwise
has_hit, snapped_location, snapped_normal, face_index, object, matrix = try_has_hit, try_snapped_location, try_snapped_normal, try_face_index, try_object, try_matrix
return has_hit, snapped_location, snapped_normal, face_index, object, matrix
if not (object.display_type == 'BOUNDS' or object_in_particle_collection(try_object)):# or not object.visible_get()):
return has_hit, snapped_location, snapped_normal, face_index, object, matrix
return empty_set
def mouse_raycast(context, mx, my):
r = context.region
@ -1197,6 +1213,67 @@ class ParticlesDropDialog(bpy.types.Operator):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=400)
# class MaterialDropDialog(bpy.types.Operator):
# """Tooltip"""
# bl_idname = "object.blenderkit_material_drop"
# bl_label = "BlenderKit material drop on linked objects"
# bl_options = {'REGISTER', 'INTERNAL'}
#
# asset_search_index: IntProperty(name="Asset index",
# description="Index of the asset in asset bar",
# default=0,
# )
#
# model_location: FloatVectorProperty(name="Location",
# default=(0, 0, 0))
#
# model_rotation: FloatVectorProperty(name="Rotation",
# default=(0, 0, 0),
# subtype='QUATERNION')
#
# target_object: StringProperty(
# name="Target object",
# description="The object to which the particles will get applied",
# default="", options={'SKIP_SAVE'})
#
# target_material_slot: IntProperty(name="Target material slot",
# description="Index of the material on the object to be changed",
# default=0,
# )
#
# @classmethod
# def poll(cls, context):
# return True
#
# def draw(self, context):
# layout = self.layout
# message = "This asset is linked to the scene from an external file and cannot have material appended." \
# " Do you want to bring it into Blender Scene?"
# utils.label_multiline(layout, text=message, width=400)
#
# def execute(self, context):
# for c in bpy.data.collections:
# for o in c.objects:
# if o.name != self.target_object:
# continue;
# for empty in bpy.context.visible_objects:
# if not(empty.instance_type == 'COLLECTION' and empty.instance_collection == c):
# continue;
# utils.activate(empty)
# break;
# bpy.ops.object.blenderkit_bring_to_scene()
# bpy.ops.scene.blenderkit_download(True,
# # asset_type=ui_props.asset_type,
# asset_index=self.asset_search_index,
# model_location=self.model_rotation,
# model_rotation=self.model_rotation,
# target_object=self.target_object,
# material_target_slot = self.target_slot)
# return {'FINISHED'}
#
# def invoke(self, context, event):
# 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'''
@ -1707,7 +1784,7 @@ class AssetBarOperator(bpy.types.Operator):
ui_props = context.scene.blenderkitUI
sr = bpy.context.window_manager.get('search results')
if self.do_search or sr is None:
if self.do_search:
# we erase search keywords for cateogry search now, since these combinations usually return nothing now.
# when the db gets bigger, this can be deleted.
if self.category != '':
@ -1715,11 +1792,8 @@ class AssetBarOperator(bpy.types.Operator):
sprops.search_keywords = ''
search.search(category=self.category)
if sr is None:
bpy.context.window_manager['search results'] = []
if ui_props.assetbar_on:
# we don't want to run the assetbar many times, that's why it has a switch on/off behaviour,
# we don't want to run the assetbar more than once, that's why it has a switch on/off behaviour,
# unless being called with 'keep_running' prop.
if not self.keep_running:
# this sends message to the originally running operator, so it quits, and then it ends this one too.
@ -1738,7 +1812,8 @@ class AssetBarOperator(bpy.types.Operator):
ui_props.assetbar_on = True
ui_props.turn_off = False
if sr is None:
bpy.context.window_manager['search results'] = []
if context.area.type != 'VIEW_3D':
self.report({'WARNING'}, "View3D not found, cannot run operator")
@ -1875,6 +1950,8 @@ class AssetDragOperator(bpy.types.Operator):
temp_mesh = object_eval.to_mesh()
target_slot = temp_mesh.polygons[self.face_index].material_index
object_eval.to_mesh_clear()
# elif object.is_library_indirect:#case for bring to scene objects, will be solved through prefs and direct
# action
else:
self.report({'WARNING'}, "Invalid or library object as input:")
target_object = ''

View File

@ -546,7 +546,10 @@ class VIEW3D_PT_blenderkit_profile(Panel):
if me is not None:
me = me['user']
# user name
layout.label(text='Me: %s %s' % (me['firstName'], me['lastName']))
if len(me['firstName'])>0 or len(me['lastName'])>0:
layout.label(text=f"Me: {me['firstName']} {me['lastName']}")
else:
layout.label(text=f"Me: {me['email']}")
# layout.label(text='Email: %s' % (me['email']))
# plan information
@ -1520,10 +1523,18 @@ class UrlPopupDialog(bpy.types.Operator):
def draw(self, context):
layout = self.layout
utils.label_multiline(layout, text=self.message)
utils.label_multiline(layout, text=self.message, width = 300)
layout.active_default = True
op = layout.operator("wm.url_open", text=self.link_text, icon='QUESTION')
if not utils.user_logged_in():
utils.label_multiline(layout,
text='Already subscribed? You need to login to access your Full Plan.',
width = 300)
layout.operator_context = 'EXEC_DEFAULT'
layout.operator("wm.blenderkit_login", text="Login",
icon='URL').signup = False
op.url = self.url
def execute(self, context):
@ -1533,7 +1544,7 @@ class UrlPopupDialog(bpy.types.Operator):
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
return wm.invoke_props_dialog(self,width = 300)
class LoginPopupDialog(bpy.types.Operator):
@ -1688,7 +1699,12 @@ def header_search_draw(self, context):
layout.prop(props, "search_keywords", text="", icon='VIEWZOOM')
draw_assetbar_show_hide(layout, props)
def ui_message(title, message):
def draw_message(self, context):
layout = self.layout
utils.label_multiline(layout, text=message)
bpy.context.window_manager.popup_menu(draw_message, title=title, icon='INFO')
# We can store multiple preview collections here,
# however in this example we only store "main"
preview_collections = {}

View File

@ -673,7 +673,6 @@ class FastMetadata(bpy.types.Operator):
except Exception as e:
print(e)
self.message = f"Fast edit metadata of {asset_data['name']}"
self.message = str(cat_path)
self.name = asset_data['displayName']
self.description = asset_data['description']
self.tags = ','.join(asset_data['tags'])