diff options
author | Vilém Duha <vilda.novak@gmail.com> | 2020-12-22 16:45:40 +0300 |
---|---|---|
committer | Vilém Duha <vilda.novak@gmail.com> | 2020-12-22 16:45:51 +0300 |
commit | 10ae0f7a5a84b0d9ac8ccbb4abe97e42ab6ca9d1 (patch) | |
tree | b742a78c088cd9baa7d1252054f5595f700dbc4b /blenderkit | |
parent | 105d485a0327976e12b34f23373079b3b2c4d834 (diff) |
BlenderKit: multiple improvements:
HDR and scene asset types - These are now available when user enables experimental features in addon preferences.
Refactor Upload - no Blender instance isn't launched when not needed and everything happens in a thread, so no waiting for the user when e.g. updating metadata.
improve pasted asset_id string
Change asset type picking interface to fit more asset types
Notify user about brush mode needs
Update categories file
Diffstat (limited to 'blenderkit')
-rw-r--r-- | blenderkit/__init__.py | 110 | ||||
-rw-r--r-- | blenderkit/append_link.py | 75 | ||||
-rw-r--r-- | blenderkit/bg_blender.py | 14 | ||||
-rw-r--r-- | blenderkit/categories.py | 2 | ||||
-rw-r--r-- | blenderkit/data/categories.json | 1578 | ||||
-rw-r--r-- | blenderkit/download.py | 30 | ||||
-rw-r--r-- | blenderkit/image_utils.py | 84 | ||||
-rw-r--r-- | blenderkit/paths.py | 4 | ||||
-rw-r--r-- | blenderkit/ratings.py | 7 | ||||
-rw-r--r-- | blenderkit/resolutions.py | 33 | ||||
-rw-r--r-- | blenderkit/search.py | 36 | ||||
-rw-r--r-- | blenderkit/ui.py | 25 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 73 | ||||
-rw-r--r-- | blenderkit/upload.py | 510 | ||||
-rw-r--r-- | blenderkit/upload_bg.py | 142 | ||||
-rw-r--r-- | blenderkit/utils.py | 28 |
16 files changed, 2204 insertions, 547 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index b02985e0..d69f3e0d 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -199,9 +199,6 @@ thumbnail_resolutions = ( ) - - - def switch_search_results(self, context): s = bpy.context.scene props = s.blenderkitUI @@ -211,6 +208,9 @@ def switch_search_results(self, context): elif props.asset_type == 'SCENE': s['search results'] = s.get('bkit scene search') s['search results orig'] = s.get('bkit scene search orig') + elif props.asset_type == 'HDR': + s['search results'] = s.get('bkit hdr search') + s['search results orig'] = s.get('bkit hdr search orig') elif props.asset_type == 'MATERIAL': s['search results'] = s.get('bkit material search') s['search results orig'] = s.get('bkit material search orig') @@ -220,7 +220,14 @@ def switch_search_results(self, context): elif props.asset_type == 'BRUSH': s['search results'] = s.get('bkit brush search') s['search results orig'] = s.get('bkit brush search orig') + if not(context.sculpt_object or context.image_paint_object): + ui.add_report( + 'Switch to paint or sculpt mode to search in BlenderKit brushes.') + search.load_previews() + if s['search results'] == None: + search.search_update(self, context) + def asset_type_callback(self, context): @@ -229,27 +236,47 @@ def asset_type_callback(self, context): items for Enum property, depending on the down_up property - BlenderKit is either in search or in upload mode. ''' - if self.down_up == 'SEARCH': - items = ( - ('MODEL', 'Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0), - # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1), - ('MATERIAL', 'Materials', 'Find materials in the BlenderKit online database', 'MATERIAL', 2), - # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3), - ('BRUSH', 'Brushes', 'Find brushes in the BlenderKit online database', 'BRUSH_DATA', 3) - ) + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + + if user_preferences.experimental_features: + if self.down_up == 'SEARCH': + items = ( + ('MODEL', 'Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0), + ('MATERIAL', 'Materials', 'Find materials in the BlenderKit online database', 'MATERIAL', 2), + # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3), + ('SCENE', 'Scenes', 'Browse scenes', 'SCENE_DATA', 3), + ('HDR', 'Hdrs', 'Browse hdrs', 'WORLD', 4), + ('BRUSH', 'Brushes', 'Find brushes in the BlenderKit online database', 'BRUSH_DATA', 5) + ) + else: + items = ( + ('MODEL', 'Model', 'Upload a model to BlenderKit', 'OBJECT_DATAMODE', 0), + # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1), + ('MATERIAL', 'Material', 'Upload a material to BlenderKit', 'MATERIAL', 2), + # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3), + ('SCENE', 'Scenes', 'Browse scenes', 'SCENE_DATA', 3), + ('HDR', 'Hdrs', 'Browse hdrs', 'WORLD', 4), + ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 5) + ) else: - items = ( - ('MODEL', 'Model', 'Upload a model to BlenderKit', 'OBJECT_DATAMODE', 0), - # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1), - ('MATERIAL', 'Material', 'Upload a material to BlenderKit', 'MATERIAL', 2), - # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3), - ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 3) - ) + if self.down_up == 'SEARCH': + items = ( + ('MODEL', 'Models', 'Find models in the BlenderKit online database', 'OBJECT_DATAMODE', 0), + ('MATERIAL', 'Materials', 'Find materials in the BlenderKit online database', 'MATERIAL', 2), + # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3), + ('BRUSH', 'Brushes', 'Find brushes in the BlenderKit online database', 'BRUSH_DATA', 5) + ) + else: + items = ( + ('MODEL', 'Model', 'Upload a model to BlenderKit', 'OBJECT_DATAMODE', 0), + # ('SCENE', 'SCENE', 'Browse scenes', 'SCENE_DATA', 1), + ('MATERIAL', 'Material', 'Upload a material to BlenderKit', 'MATERIAL', 2), + # ('TEXTURE', 'Texture', 'Browse textures', 'TEXTURE', 3), + ('BRUSH', 'Brush', 'Upload a brush to BlenderKit', 'BRUSH_DATA', 5) + ) return items - - class BlenderKitUIProps(PropertyGroup): down_up: EnumProperty( name="Download vs Upload", @@ -363,6 +390,15 @@ class BlenderKitUIProps(PropertyGroup): dragging_rating_work_hours: BoolProperty(name="Dragging Rating Work Hours", default=False) last_rating_time: FloatProperty(name="Last Rating Time", default=0.0) + hdr_upload_image: PointerProperty(name='Upload HDR', + type=bpy.types.Image, + description='Pick an image to upload') + + # StringProperty( + # name="Upload HDR", + # description="Active HDR image to upload", + # default="") + def search_procedural_update(self, context): if self.search_procedural in ('PROCEDURAL', 'BOTH'): @@ -867,6 +903,10 @@ class BlenderKitBrushSearchProps(PropertyGroup, BlenderKitCommonSearchProps): ) +class BlenderKitHDRUploadProps(PropertyGroup, BlenderKitCommonUploadProps): + pass; + + class BlenderKitBrushUploadProps(PropertyGroup, BlenderKitCommonUploadProps): mode: EnumProperty( name="Mode", @@ -1274,7 +1314,6 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps): free_only: BoolProperty(name="Free only", description="Show only free models", default=False, update=search.search_update) - # CONDITION search_condition: EnumProperty( items=conditions, @@ -1395,6 +1434,15 @@ class BlenderKitModelSearchProps(PropertyGroup, BlenderKitCommonSearchProps): ) +class BlenderKitHDRSearchProps(PropertyGroup, BlenderKitCommonSearchProps): + search_keywords: StringProperty( + name="Search", + description="Search for these keywords", + default="", + update=search.search_update + ) + + class BlenderKitSceneSearchProps(PropertyGroup, BlenderKitCommonSearchProps): search_keywords: StringProperty( name="Search", @@ -1602,6 +1650,12 @@ class BlenderKitAddonPreferences(AddonPreferences): update=utils.save_prefs ) + experimental_features: BoolProperty( + name="Enable experimental features", + description="Enable all experimental features of BlenderKit. Use at your own risk.", + default=False, + update=utils.save_prefs + ) # allow_proximity : BoolProperty( # name="allow proximity data reports", # description="This sends anonymized proximity data \n \ @@ -1642,6 +1696,7 @@ class BlenderKitAddonPreferences(AddonPreferences): layout.prop(self, "search_in_header") if bpy.context.preferences.view.show_developer_ui: layout.prop(self, "use_timers") + layout.prop(self, "experimental_features") # registration @@ -1656,6 +1711,9 @@ classes = ( BlenderKitSceneSearchProps, BlenderKitSceneUploadProps, + BlenderKitHDRSearchProps, + BlenderKitHDRUploadProps, + BlenderKitMaterialUploadProps, BlenderKitMaterialSearchProps, @@ -1691,6 +1749,14 @@ def register(): bpy.types.Scene.bkit_ratings = PointerProperty( # for uploads, not now... type=BlenderKitRatingProps) + # HDRs + bpy.types.Scene.blenderkit_HDR = PointerProperty( + type=BlenderKitHDRSearchProps) + bpy.types.Image.blenderkit = PointerProperty( # for uploads, not now... + type=BlenderKitHDRUploadProps) + bpy.types.Image.bkit_ratings = PointerProperty( # for uploads, not now... + type=BlenderKitRatingProps) + # MATERIALS bpy.types.Scene.blenderkit_mat = PointerProperty( type=BlenderKitMaterialSearchProps) @@ -1755,11 +1821,13 @@ def unregister(): del bpy.types.Scene.blenderkit_models del bpy.types.Scene.blenderkit_scene + del bpy.types.Scene.blenderkit_HDR del bpy.types.Scene.blenderkit_brush del bpy.types.Scene.blenderkit_mat del bpy.types.Scene.blenderkit del bpy.types.Object.blenderkit + del bpy.types.Image.blenderkit del bpy.types.Material.blenderkit del bpy.types.Brush.blenderkit diff --git a/blenderkit/append_link.py b/blenderkit/append_link.py index 66fa711f..c80027c5 100644 --- a/blenderkit/append_link.py +++ b/blenderkit/append_link.py @@ -96,6 +96,81 @@ def append_scene(file_name, scenename=None, link=False, fake_user=False): return scene +def get_node_sure(node_tree, ntype=''): + ''' + Gets a node of certain type, but creates a new one if not pre + ''' + node = None + for n in node_tree.nodes: + if ntype == n.bl_rna.identifier: + node = n + return node + if not node: + node = node_tree.nodes.new(type=ntype) + + return node + +def hdr_swap(name, hdr): + ''' + Try to replace the hdr in current world setup. If this fails, create a new world. + :param name: Name of the resulting world (renamse the current one if swap is successfull) + :param hdr: Image type + :return: None + ''' + w = bpy.context.scene.world + w.use_nodes = True + w.name = name + nt = w.node_tree + for n in nt.nodes: + if 'ShaderNodeTexEnvironment' == n.bl_rna.identifier: + env_node = n + env_node.image = hdr + return + new_hdr_world(name,hdr) + + +def new_hdr_world(name, hdr): + ''' + creates a new world, links in the hdr with mapping node, and links the world to scene + :param name: Name of the world datablock + :param hdr: Image type + :return: None + ''' + w = bpy.data.worlds.new(name=name) + w.use_nodes = True + bpy.context.scene.world = w + + nt = w.node_tree + env_node = nt.nodes.new(type='ShaderNodeTexEnvironment') + env_node.image = hdr + background = get_node_sure(nt, 'ShaderNodeBackground') + tex_coord = get_node_sure(nt, 'ShaderNodeTexCoord') + mapping = get_node_sure(nt, 'ShaderNodeMapping') + + nt.links.new(env_node.outputs['Color'], background.inputs['Color']) + nt.links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector']) + nt.links.new(mapping.outputs['Vector'], env_node.inputs['Vector']) + env_node.location.x = -400 + mapping.location.x = -600 + tex_coord.location.x = -800 + + +def load_HDR(file_name, name): + '''Load a HDR into file and link it to scene world. ''' + already_linked = False + for i in bpy.data.images: + if i.filepath == file_name: + hdr = i + already_linked = True + break; + + if not already_linked: + hdr = bpy.data.images.load(file_name) + + hdr_swap(name, hdr) + return hdr + + def link_collection(file_name, obnames=[], location=(0, 0, 0), link=False, parent = None, **kwargs): '''link an instanced group - model type asset''' sel = utils.selection_get() diff --git a/blenderkit/bg_blender.py b/blenderkit/bg_blender.py index a8597675..128cf457 100644 --- a/blenderkit/bg_blender.py +++ b/blenderkit/bg_blender.py @@ -55,7 +55,9 @@ class threadCom: # object passed to threads to read background process stdout i def threadread(tcom): - '''reads stdout of background process, done this way to have it non-blocking. this threads basically waits for a stdout line to come in, fills the data, dies.''' + '''reads stdout of background process. + this threads basically waits for a stdout line to come in, + fills the data, dies.''' found = False while not found: inline = tcom.proc.stdout.readline() @@ -155,6 +157,7 @@ process_types = ( process_sources = ( ('MODEL', 'Model', 'set of objects'), ('SCENE', 'Scene', 'set of scenes'), + ('HDR', 'HDR', 'HDR image'), ('MATERIAL', 'Material', 'any .blend Material'), ('TEXTURE', 'Texture', 'a texture, or texture set'), ('BRUSH', 'Brush', 'brush, can be any type of blender brush'), @@ -203,9 +206,18 @@ class KillBgProcess(bpy.types.Operator): if tcom.process_type == self.process_type: source = eval(tcom.eval_path) kill = False + #TODO HDR - add killing of process if source.bl_rna.name == 'Object' and self.process_source == 'MODEL': if source.name == bpy.context.active_object.name: kill = True + if source.bl_rna.name == 'Scene' and self.process_source == 'SCENE': + if source.name == bpy.context.scene.name: + kill = True + if source.bl_rna.name == 'Image' and self.process_source == 'HDR': + ui_props = bpy.context.scene.blenderkitUI + if source.name == ui_props.hdr_upload_image.name: + kill = False + if source.bl_rna.name == 'Material' and self.process_source == 'MATERIAL': if source.name == bpy.context.active_object.active_material.name: kill = True diff --git a/blenderkit/categories.py b/blenderkit/categories.py index 07c811d8..43d6f2ba 100644 --- a/blenderkit/categories.py +++ b/blenderkit/categories.py @@ -104,6 +104,7 @@ def get_upload_asset_type(self): typemapper = { bpy.types.Object.blenderkit: 'model', bpy.types.Scene.blenderkit: 'scene', + bpy.types.Image.blenderkit: 'hdr', bpy.types.Material.blenderkit: 'material', bpy.types.Brush.blenderkit: 'brush' } @@ -174,6 +175,7 @@ def load_categories(): wm['active_category'] = { 'MODEL': ['model'], 'SCENE': ['scene'], + 'HDR': ['hdr'], 'MATERIAL': ['material'], 'BRUSH': ['brush'], } diff --git a/blenderkit/data/categories.json b/blenderkit/data/categories.json index d6286050..8bf6c85c 100644 --- a/blenderkit/data/categories.json +++ b/blenderkit/data/categories.json @@ -82,6 +82,23 @@ "assetCountCumulative": 8 }, { + "name": "crack", + "slug": "crack", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "crack", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "cut", "slug": "cut", "active": true, @@ -129,8 +146,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 1, - "assetCountCumulative": 1 + "assetCount": 2, + "assetCountCumulative": 2 }, { "name": "fabric", @@ -197,8 +214,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 9, - "assetCountCumulative": 9 + "assetCount": 10, + "assetCountCumulative": 10 }, { "name": "landscape", @@ -231,8 +248,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 1, - "assetCountCumulative": 1 + "assetCount": 7, + "assetCountCumulative": 7 + }, + { + "name": "nature", + "slug": "nature-brush", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "nature", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "pattern", @@ -248,8 +282,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 7, - "assetCountCumulative": 7 + "assetCount": 9, + "assetCountCumulative": 9 }, { "name": "rock", @@ -269,6 +303,40 @@ "assetCountCumulative": 8 }, { + "name": "rust", + "slug": "rust-brush", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "rust", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "sculpture", + "slug": "sculpture-brush", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "sculpture", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "stitches", "slug": "stitches", "active": true, @@ -286,6 +354,23 @@ "assetCountCumulative": 12 }, { + "name": "stone", + "slug": "stone-brush", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "stone", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "tree", "slug": "tree-brush", "active": true, @@ -301,10 +386,79 @@ "children": [], "assetCount": 4, "assetCountCumulative": 4 + }, + { + "name": "wood", + "slug": "wood-brush", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "wood", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + } + ], + "assetCount": 104, + "assetCountCumulative": 104 + }, + { + "name": "HDR", + "slug": "hdr", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "HDR", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [ + { + "name": "Indoor", + "slug": "indoor", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "Indoor", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "Outdoor", + "slug": "hdr-outdoor", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "Outdoor", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 } ], - "assetCount": 94, - "assetCountCumulative": 94 + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "material", @@ -334,8 +488,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 36, - "assetCountCumulative": 36 + "assetCount": 24, + "assetCountCumulative": 24 }, { "name": "asphalt", @@ -351,8 +505,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 36, - "assetCountCumulative": 36 + "assetCount": 45, + "assetCountCumulative": 45 }, { "name": "bricks", @@ -368,8 +522,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 64, - "assetCountCumulative": 64 + "assetCount": 106, + "assetCountCumulative": 106 }, { "name": "ceramic", @@ -385,8 +539,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 16, - "assetCountCumulative": 16 + "assetCount": 33, + "assetCountCumulative": 33 }, { "name": "concrete", @@ -402,8 +556,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 64, - "assetCountCumulative": 64 + "assetCount": 89, + "assetCountCumulative": 89 }, { "name": "dirt", @@ -436,8 +590,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 169, - "assetCountCumulative": 169 + "assetCount": 192, + "assetCountCumulative": 192 }, { "name": "floor", @@ -453,8 +607,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 41, - "assetCountCumulative": 41 + "assetCount": 68, + "assetCountCumulative": 68 }, { "name": "food", @@ -470,8 +624,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 30, - "assetCountCumulative": 30 + "assetCount": 35, + "assetCountCumulative": 35 }, { "name": "fx", @@ -487,8 +641,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 19, - "assetCountCumulative": 19 + "assetCount": 28, + "assetCountCumulative": 28 }, { "name": "glass", @@ -504,8 +658,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 28, - "assetCountCumulative": 28 + "assetCount": 50, + "assetCountCumulative": 50 + }, + { + "name": "grass", + "slug": "grass", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "grass", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 4, + "assetCountCumulative": 4 }, { "name": "ground", @@ -521,8 +692,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 70, - "assetCountCumulative": 70 + "assetCount": 96, + "assetCountCumulative": 96 + }, + { + "name": "human", + "slug": "human", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "human", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "ice", @@ -538,8 +726,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 20, - "assetCountCumulative": 20 + "assetCount": 25, + "assetCountCumulative": 25 }, { "name": "leather", @@ -555,8 +743,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 37, - "assetCountCumulative": 37 + "assetCount": 53, + "assetCountCumulative": 53 }, { "name": "liquid", @@ -572,8 +760,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 7, - "assetCountCumulative": 7 + "assetCount": 10, + "assetCountCumulative": 10 + }, + { + "name": "marble", + "slug": "marble", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "marble", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "metal", @@ -589,8 +794,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 119, - "assetCountCumulative": 119 + "assetCount": 214, + "assetCountCumulative": 214 }, { "name": "organic", @@ -606,8 +811,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 29, - "assetCountCumulative": 29 + "assetCount": 40, + "assetCountCumulative": 40 }, { "name": "ornaments", @@ -623,8 +828,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 3, - "assetCountCumulative": 3 + "assetCount": 14, + "assetCountCumulative": 14 }, { "name": "paper", @@ -640,8 +845,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 30, - "assetCountCumulative": 30 + "assetCount": 41, + "assetCountCumulative": 41 }, { "name": "paving", @@ -657,8 +862,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 32, - "assetCountCumulative": 32 + "assetCount": 39, + "assetCountCumulative": 39 }, { "name": "plaster", @@ -674,8 +879,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 34, - "assetCountCumulative": 34 + "assetCount": 53, + "assetCountCumulative": 53 }, { "name": "plastic", @@ -691,8 +896,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 25, - "assetCountCumulative": 25 + "assetCount": 41, + "assetCountCumulative": 41 }, { "name": "rock", @@ -708,8 +913,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 21, - "assetCountCumulative": 21 + "assetCount": 39, + "assetCountCumulative": 39 }, { "name": "roofing", @@ -725,8 +930,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 12, - "assetCountCumulative": 12 + "assetCount": 16, + "assetCountCumulative": 16 }, { "name": "rubber", @@ -759,8 +964,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 17, - "assetCountCumulative": 17 + "assetCount": 20, + "assetCountCumulative": 20 }, { "name": "sand", @@ -776,8 +981,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 16, - "assetCountCumulative": 16 + "assetCount": 20, + "assetCountCumulative": 20 + }, + { + "name": "soil", + "slug": "soil", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "soil", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "stone", @@ -793,8 +1015,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 117, - "assetCountCumulative": 117 + "assetCount": 136, + "assetCountCumulative": 136 }, { "name": "tech", @@ -810,8 +1032,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 34, - "assetCountCumulative": 34 + "assetCount": 63, + "assetCountCumulative": 63 }, { "name": "tiles", @@ -827,8 +1049,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 84, - "assetCountCumulative": 84 + "assetCount": 118, + "assetCountCumulative": 118 }, { "name": "wood", @@ -844,12 +1066,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 121, - "assetCountCumulative": 121 + "assetCount": 193, + "assetCountCumulative": 193 } ], - "assetCount": 1365, - "assetCountCumulative": 1365 + "assetCount": 1939, + "assetCountCumulative": 1939 }, { "name": "model", @@ -893,12 +1115,97 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 20, - "assetCountCumulative": 20 + "assetCount": 10, + "assetCountCumulative": 10 + }, + { + "name": "helicopter", + "slug": "helicopter", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "helicopter", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "historic", + "slug": "historic-aircraft", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "historic", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "jet", + "slug": "jet", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "jet", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "part", + "slug": "part-aircraft", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "part", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "private", + "slug": "private", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "private", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 } ], - "assetCount": 20, - "assetCountCumulative": 20 + "assetCount": 10, + "assetCountCumulative": 10 }, { "name": "architecture", @@ -928,12 +1235,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 133, - "assetCountCumulative": 133 + "assetCount": 225, + "assetCountCumulative": 225 } ], - "assetCount": 137, - "assetCountCumulative": 137 + "assetCount": 229, + "assetCountCumulative": 229 }, { "name": "art", @@ -950,6 +1257,40 @@ "metaExtra": "", "children": [ { + "name": "design", + "slug": "design", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "design", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 6, + "assetCountCumulative": 6 + }, + { + "name": "drawing", + "slug": "drawing", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "drawing", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "literature", "slug": "literature", "active": true, @@ -963,8 +1304,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 9, - "assetCountCumulative": 9 + "assetCount": 17, + "assetCountCumulative": 17 }, { "name": "painting", @@ -980,8 +1321,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 15, - "assetCountCumulative": 15 + "assetCount": 39, + "assetCountCumulative": 39 + }, + { + "name": "photo", + "slug": "photo", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "photo", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "sculpture", @@ -997,8 +1355,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 15, - "assetCountCumulative": 15 + "assetCount": 25, + "assetCountCumulative": 25 }, { "name": "supplies", @@ -1014,12 +1372,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 8, - "assetCountCumulative": 8 + "assetCount": 7, + "assetCountCumulative": 7 } ], - "assetCount": 49, - "assetCountCumulative": 49 + "assetCount": 95, + "assetCountCumulative": 95 }, { "name": "character", @@ -1049,8 +1407,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 14, - "assetCountCumulative": 14 + "assetCount": 16, + "assetCountCumulative": 16 + }, + { + "name": "child", + "slug": "child", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "child", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "clothing", @@ -1066,8 +1441,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 9, - "assetCountCumulative": 9 + "assetCount": 18, + "assetCountCumulative": 18 }, { "name": "fantasy", @@ -1083,8 +1458,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 2, - "assetCountCumulative": 2 + "assetCount": 10, + "assetCountCumulative": 10 }, { "name": "man", @@ -1100,8 +1475,42 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 13, - "assetCountCumulative": 13 + "assetCount": 18, + "assetCountCumulative": 18 + }, + { + "name": "people", + "slug": "people", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "people", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "sci-fi", + "slug": "sci-fi-character", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "sci-fi", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "woman", @@ -1117,12 +1526,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 7, - "assetCountCumulative": 7 + "assetCount": 8, + "assetCountCumulative": 8 } ], - "assetCount": 46, - "assetCountCumulative": 46 + "assetCount": 71, + "assetCountCumulative": 71 }, { "name": "exterior", @@ -1152,8 +1561,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 24, - "assetCountCumulative": 24 + "assetCount": 31, + "assetCountCumulative": 31 }, { "name": "cityspace", @@ -1169,8 +1578,76 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 24, - "assetCountCumulative": 24 + "assetCount": 30, + "assetCountCumulative": 30 + }, + { + "name": "historic", + "slug": "historic", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "historic", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "house", + "slug": "house", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "house", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "industrial", + "slug": "industrial-exterior", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "industrial", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "landmark", + "slug": "landmark", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "landmark", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "landscape", @@ -1186,8 +1663,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 35, - "assetCountCumulative": 35 + "assetCount": 43, + "assetCountCumulative": 43 }, { "name": "public", @@ -1203,8 +1680,42 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 12, - "assetCountCumulative": 12 + "assetCount": 22, + "assetCountCumulative": 22 + }, + { + "name": "sci-fi", + "slug": "sci-fi", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "sci-fi", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "stadium", + "slug": "stadium", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "stadium", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "street", @@ -1220,12 +1731,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 35, - "assetCountCumulative": 35 + "assetCount": 45, + "assetCountCumulative": 45 } ], - "assetCount": 132, - "assetCountCumulative": 132 + "assetCount": 174, + "assetCountCumulative": 174 }, { "name": "food & drink", @@ -1255,8 +1766,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 33, - "assetCountCumulative": 33 + "assetCount": 99, + "assetCountCumulative": 99 }, { "name": "drink", @@ -1272,8 +1783,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 27, - "assetCountCumulative": 27 + "assetCount": 83, + "assetCountCumulative": 83 }, { "name": "drugs", @@ -1306,12 +1817,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 47, - "assetCountCumulative": 47 + "assetCount": 73, + "assetCountCumulative": 73 } ], - "assetCount": 117, - "assetCountCumulative": 117 + "assetCount": 269, + "assetCountCumulative": 269 }, { "name": "furniture", @@ -1341,8 +1852,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 21, - "assetCountCumulative": 21 + "assetCount": 36, + "assetCountCumulative": 36 }, { "name": "carpet", @@ -1358,8 +1869,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 10, - "assetCountCumulative": 10 + "assetCount": 13, + "assetCountCumulative": 13 }, { "name": "desk", @@ -1375,8 +1886,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 21, - "assetCountCumulative": 21 + "assetCount": 27, + "assetCountCumulative": 27 }, { "name": "fireplace", @@ -1409,8 +1920,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 106, - "assetCountCumulative": 106 + "assetCount": 143, + "assetCountCumulative": 143 }, { "name": "seating", @@ -1426,8 +1937,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 179, - "assetCountCumulative": 179 + "assetCount": 276, + "assetCountCumulative": 276 }, { "name": "shelving", @@ -1443,8 +1954,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 38, - "assetCountCumulative": 38 + "assetCount": 65, + "assetCountCumulative": 65 }, { "name": "sofa", @@ -1460,8 +1971,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 57, - "assetCountCumulative": 57 + "assetCount": 101, + "assetCountCumulative": 101 }, { "name": "storage", @@ -1477,8 +1988,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 65, - "assetCountCumulative": 65 + "assetCount": 102, + "assetCountCumulative": 102 }, { "name": "table", @@ -1494,12 +2005,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 106, - "assetCountCumulative": 106 + "assetCount": 184, + "assetCountCumulative": 184 } ], - "assetCount": 611, - "assetCountCumulative": 611 + "assetCount": 955, + "assetCountCumulative": 955 }, { "name": "industrial", @@ -1516,6 +2027,57 @@ "metaExtra": "", "children": [ { + "name": "agriculture", + "slug": "agriculture", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "agriculture", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "communication", + "slug": "communication", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "communication", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "construction", + "slug": "construction", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "construction", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "container", "slug": "container-industrial", "active": true, @@ -1529,8 +2091,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 48, - "assetCountCumulative": 48 + "assetCount": 66, + "assetCountCumulative": 66 + }, + { + "name": "machine", + "slug": "machine", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "machine", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 18, + "assetCountCumulative": 18 }, { "name": "tool", @@ -1546,12 +2125,29 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 29, - "assetCountCumulative": 29 + "assetCount": 62, + "assetCountCumulative": 62 + }, + { + "name": "utility", + "slug": "utility-industrial", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "part", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 } ], - "assetCount": 85, - "assetCountCumulative": 85 + "assetCount": 157, + "assetCountCumulative": 157 }, { "name": "interior", @@ -1581,8 +2177,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 96, - "assetCountCumulative": 96 + "assetCount": 133, + "assetCountCumulative": 133 }, { "name": "bedroom", @@ -1598,8 +2194,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 64, - "assetCountCumulative": 64 + "assetCount": 83, + "assetCountCumulative": 83 }, { "name": "decoration", @@ -1615,8 +2211,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 291, - "assetCountCumulative": 291 + "assetCount": 385, + "assetCountCumulative": 385 }, { "name": "hall", @@ -1632,8 +2228,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 8, - "assetCountCumulative": 8 + "assetCount": 12, + "assetCountCumulative": 12 }, { "name": "kids room", @@ -1649,8 +2245,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 42, - "assetCountCumulative": 42 + "assetCount": 47, + "assetCountCumulative": 47 }, { "name": "kitchen", @@ -1666,8 +2262,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 181, - "assetCountCumulative": 181 + "assetCount": 258, + "assetCountCumulative": 258 }, { "name": "living room", @@ -1683,8 +2279,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 127, - "assetCountCumulative": 127 + "assetCount": 170, + "assetCountCumulative": 170 }, { "name": "office", @@ -1700,8 +2296,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 48, - "assetCountCumulative": 48 + "assetCount": 60, + "assetCountCumulative": 60 }, { "name": "utility", @@ -1717,12 +2313,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 74, - "assetCountCumulative": 74 + "assetCount": 150, + "assetCountCumulative": 150 } ], - "assetCount": 932, - "assetCountCumulative": 932 + "assetCount": 1299, + "assetCountCumulative": 1299 }, { "name": "military", @@ -1739,6 +2335,23 @@ "metaExtra": "", "children": [ { + "name": "air", + "slug": "air", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "air", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 2, + "assetCountCumulative": 2 + }, + { "name": "equipment", "slug": "equipment", "active": true, @@ -1752,8 +2365,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 3, - "assetCountCumulative": 3 + "assetCount": 5, + "assetCountCumulative": 5 + }, + { + "name": "ground", + "slug": "ground", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "ground", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "historic", @@ -1769,8 +2399,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 6, - "assetCountCumulative": 6 + "assetCount": 9, + "assetCountCumulative": 9 + }, + { + "name": "naval", + "slug": "naval", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "naval", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "weapon", @@ -1786,12 +2433,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 29, - "assetCountCumulative": 29 + "assetCount": 44, + "assetCountCumulative": 44 } ], - "assetCount": 40, - "assetCountCumulative": 40 + "assetCount": 64, + "assetCountCumulative": 64 }, { "name": "music", @@ -1821,12 +2468,63 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 13, - "assetCountCumulative": 13 + "assetCount": 22, + "assetCountCumulative": 22 + }, + { + "name": "instruments", + "slug": "instruments", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "instruments", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 5, + "assetCountCumulative": 5 + }, + { + "name": "stage", + "slug": "stage", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "stage", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "studio", + "slug": "studio", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "studio", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 } ], - "assetCount": 13, - "assetCountCumulative": 13 + "assetCount": 27, + "assetCountCumulative": 27 }, { "name": "nature", @@ -1856,8 +2554,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 7, - "assetCountCumulative": 7 + "assetCount": 11, + "assetCountCumulative": 11 }, { "name": "atmosphere", @@ -1890,8 +2588,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 35, - "assetCountCumulative": 35 + "assetCount": 47, + "assetCountCumulative": 47 }, { "name": "plant", @@ -1907,8 +2605,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 48, - "assetCountCumulative": 48 + "assetCount": 80, + "assetCountCumulative": 80 }, { "name": "tree", @@ -1924,12 +2622,29 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 47, - "assetCountCumulative": 47 + "assetCount": 109, + "assetCountCumulative": 109 + }, + { + "name": "weather", + "slug": "weather", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "weather", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 } ], - "assetCount": 141, - "assetCountCumulative": 141 + "assetCount": 251, + "assetCountCumulative": 251 }, { "name": "space", @@ -1946,6 +2661,57 @@ "metaExtra": "", "children": [ { + "name": "astronomy", + "slug": "astronomy", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "astronomy", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "planets", + "slug": "planets", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "planets", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "sci-fi", + "slug": "sci-fi-space", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "sci-fi", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "spacecraft", "slug": "spacecraft", "active": true, @@ -1959,12 +2725,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 9, - "assetCountCumulative": 9 + "assetCount": 21, + "assetCountCumulative": 21 } ], - "assetCount": 10, - "assetCountCumulative": 10 + "assetCount": 22, + "assetCountCumulative": 22 }, { "name": "sports", @@ -1981,6 +2747,23 @@ "metaExtra": "", "children": [ { + "name": "animal", + "slug": "animal", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "animal", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { "name": "exercise", "slug": "exercise", "active": true, @@ -1994,8 +2777,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 12, - "assetCountCumulative": 12 + "assetCount": 14, + "assetCountCumulative": 14 }, { "name": "extreme", @@ -2011,8 +2794,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 4, - "assetCountCumulative": 4 + "assetCount": 7, + "assetCountCumulative": 7 }, { "name": "individual", @@ -2028,8 +2811,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 5, - "assetCountCumulative": 5 + "assetCount": 6, + "assetCountCumulative": 6 }, { "name": "outdoor", @@ -2045,8 +2828,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 4, - "assetCountCumulative": 4 + "assetCount": 8, + "assetCountCumulative": 8 }, { "name": "team", @@ -2062,12 +2845,12 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 6, - "assetCountCumulative": 6 + "assetCount": 15, + "assetCountCumulative": 15 } ], - "assetCount": 31, - "assetCountCumulative": 31 + "assetCount": 50, + "assetCountCumulative": 50 }, { "name": "technology", @@ -2084,6 +2867,23 @@ "metaExtra": "", "children": [ { + "name": "ai", + "slug": "ai", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "ai", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 6, + "assetCountCumulative": 6 + }, + { "name": "audio", "slug": "audio", "active": true, @@ -2097,8 +2897,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 16, - "assetCountCumulative": 16 + "assetCount": 33, + "assetCountCumulative": 33 }, { "name": "computer", @@ -2114,8 +2914,42 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 102, - "assetCountCumulative": 102 + "assetCount": 140, + "assetCountCumulative": 140 + }, + { + "name": "medical", + "slug": "medical", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "medical", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 2, + "assetCountCumulative": 2 + }, + { + "name": "medicine", + "slug": "medicine", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "medicine", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "phone", @@ -2131,8 +2965,8 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 8, - "assetCountCumulative": 8 + "assetCount": 15, + "assetCountCumulative": 15 }, { "name": "photography", @@ -2148,12 +2982,46 @@ "metaKeywords": "", "metaExtra": "", "children": [], + "assetCount": 11, + "assetCountCumulative": 11 + }, + { + "name": "science", + "slug": "science", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "science", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 1, + "assetCountCumulative": 1 + }, + { + "name": "video", + "slug": "video", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "video", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], "assetCount": 6, "assetCountCumulative": 6 } ], - "assetCount": 138, - "assetCountCumulative": 138 + "assetCount": 222, + "assetCountCumulative": 222 }, { "name": "vehicle", @@ -2170,6 +3038,40 @@ "metaExtra": "", "children": [ { + "name": "bicycle", + "slug": "bicycle", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "bicycle", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "bus", + "slug": "bus", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "bus", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 2, + "assetCountCumulative": 2 + }, + { "name": "car", "slug": "car", "active": true, @@ -2183,8 +3085,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 43, - "assetCountCumulative": 43 + "assetCount": 63, + "assetCountCumulative": 63 + }, + { + "name": "historic", + "slug": "historic-vehicle", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "historic", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "industrial", @@ -2200,8 +3119,25 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 5, - "assetCountCumulative": 5 + "assetCount": 7, + "assetCountCumulative": 7 + }, + { + "name": "motorcycle", + "slug": "motorcycle", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "motorcycle", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 }, { "name": "part", @@ -2217,32 +3153,222 @@ "metaKeywords": "", "metaExtra": "", "children": [], - "assetCount": 20, - "assetCountCumulative": 20 + "assetCount": 54, + "assetCountCumulative": 54 }, { - "name": "Animals", - "slug": "animals", + "name": "train", + "slug": "train", "active": true, "thumbnail": null, "thumbnailWidth": null, "thumbnailHeight": null, "order": 0, - "alternateTitle": "Animals", + "alternateTitle": "train", "alternateUrl": "", "description": "", "metaKeywords": "", "metaExtra": "", "children": [], "assetCount": 0, - "assetCountCumulative": 11 + "assetCountCumulative": 0 + }, + { + "name": "truck", + "slug": "truck", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "truck", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 } ], - "assetCount": 70, - "assetCountCumulative": 70 + "assetCount": 129, + "assetCountCumulative": 129 + }, + { + "name": "watercraft", + "slug": "watercraft", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "watercraft", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [ + { + "name": "historic", + "slug": "historic-watercraft", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "historic", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "industrial", + "slug": "industrial-watercraft", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "industrial", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "part", + "slug": "part-watercraft", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "part", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "personal", + "slug": "personal", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "personal", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "recreational", + "slug": "recreational", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "recreational", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + } + ], + "assetCount": 0, + "assetCountCumulative": 0 + } + ], + "assetCount": 4025, + "assetCountCumulative": 4025 + }, + { + "name": "texture", + "slug": "texture", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "texture", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [ + { + "name": "Animals", + "slug": "animals", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "Animals", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [ + { + "name": "Mammals", + "slug": "mammals", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "Mammals", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + }, + { + "name": "Plants", + "slug": "plants", + "active": true, + "thumbnail": null, + "thumbnailWidth": null, + "thumbnailHeight": null, + "order": 0, + "alternateTitle": "Plants", + "alternateUrl": "", + "description": "", + "metaKeywords": "", + "metaExtra": "", + "children": [], + "assetCount": 0, + "assetCountCumulative": 0 + } + ], + "assetCount": 0, + "assetCountCumulative": 12 } ], - "assetCount": 2572, - "assetCountCumulative": 2572 + "assetCount": 0, + "assetCountCumulative": 0 } ]
\ No newline at end of file diff --git a/blenderkit/download.py b/blenderkit/download.py index 8749b696..dae95a11 100644 --- a/blenderkit/download.py +++ b/blenderkit/download.py @@ -290,6 +290,8 @@ def udpate_asset_data_in_dicts(asset_data): id = asset_data['assetBaseId'] scene['assets rated'][id] = scene['assets rated'].get(id, False) sr = bpy.context.scene['search results'] + if not sr: + return; for i, r in enumerate(sr): if r['assetBaseId'] == asset_data['assetBaseId']: for f in asset_data['files']: @@ -323,6 +325,11 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None, props = scene.blenderkit asset_main = scene + if asset_data['assetType'] == 'hdr': + hdr = append_link.load_HDR(file_name = file_names[0], name = asset_data['name']) + props = hdr.blenderkit + asset_main = hdr + s = bpy.context.scene if asset_data['assetType'] == 'model': @@ -604,7 +611,7 @@ def timer_update(): if ((bpy.context.mode == 'OBJECT' and \ (at == 'model' or at == 'material'))) \ or ((at == 'brush') \ - and wm.get('appendable') == True) or at == 'scene': + and wm.get('appendable') == True) or at == 'scene' or at == 'hdr': # don't do this stuff in editmode and other modes, just wait... download_threads.remove(threaddata) @@ -646,14 +653,8 @@ def timer_update(): if not done: at = asset_data['assetType'] tcom.passargs['retry_counter'] = tcom.passargs.get('retry_counter', 0) + 1 - if at in ('model', 'material'): - download(asset_data, **tcom.passargs) - elif asset_data['assetType'] == 'material': - download(asset_data, **tcom.passargs) - elif asset_data['assetType'] == 'scene': - download(asset_data, **tcom.passargs) - elif asset_data['assetType'] == 'brush' or asset_data['assetType'] == 'texture': - download(asset_data, **tcom.passargs) + download(asset_data, **tcom.passargs) + if bpy.context.scene['search results'] is not None and done: for sres in bpy.context.scene['search results']: if asset_data['id'] == sres['id']: @@ -776,14 +777,14 @@ class Downloader(threading.Thread): # this sends the thread for processing, where another check should occur, since the file might be corrupted. tcom.downloaded = 100 utils.p('not downloading, trying to append again') - return; + return file_name = paths.get_download_filepaths(asset_data, self.resolution)[0] # prefer global dir if possible. # for k in asset_data: # print(asset_data[k]) if self.stopped(): utils.p('stopping download: ' + asset_data['name']) - return; + return download_canceled = False with open(file_name, "wb") as f: @@ -827,7 +828,7 @@ class Downloader(threading.Thread): if download_canceled: delete_unfinished_file(file_name) - return; + return # unpack the file immediately after download tcom.report = f'Unpacking files' @@ -1170,15 +1171,14 @@ def start_download(asset_data, **kwargs): 'rotation': kwargs['model_rotation']} download(asset_data, downloaders=[downloader], **kwargs) - elif asset_data['assetType'] == 'scene': - download(asset_data, **kwargs) - elif asset_data['assetType'] == 'brush' or asset_data['assetType'] == 'texture': + else: download(asset_data, **kwargs) asset_types = ( ('MODEL', 'Model', 'set of objects'), ('SCENE', 'Scene', 'scene'), + ('HDR', 'Hdr', 'hdr'), ('MATERIAL', 'Material', 'any .blend Material'), ('TEXTURE', 'Texture', 'a texture, or texture set'), ('BRUSH', 'Brush', 'brush, can be any type of blender brush'), diff --git a/blenderkit/image_utils.py b/blenderkit/image_utils.py new file mode 100644 index 00000000..b2ade804 --- /dev/null +++ b/blenderkit/image_utils.py @@ -0,0 +1,84 @@ +import bpy +import numpy +import os + +def get_orig_render_settings(): + rs = bpy.context.scene.render + ims = rs.image_settings + + vs = bpy.context.scene.view_settings + + orig_settings = { + 'file_format': ims.file_format, + 'quality': ims.quality, + 'color_mode': ims.color_mode, + 'compression': ims.compression, + 'view_transform': vs.view_transform + } + return orig_settings + + +def set_orig_render_settings(orig_settings): + rs = bpy.context.scene.render + ims = rs.image_settings + vs = bpy.context.scene.view_settings + + ims.file_format = orig_settings['file_format'] + ims.quality = orig_settings['quality'] + ims.color_mode = orig_settings['color_mode'] + ims.compression = orig_settings['compression'] + + vs.view_transform = orig_settings['view_transform'] + + +def img_save_as(img, filepath='//', file_format='JPEG', quality=90, color_mode='RGB', compression=15): + '''Uses Blender 'save render' to save images - BLender isn't really able so save images with other methods correctly.''' + + ors = get_orig_render_settings() + + rs = bpy.context.scene.render + ims = rs.image_settings + ims.file_format = file_format + ims.quality = quality + ims.color_mode = color_mode + ims.compression = compression + + img.save_render(filepath=bpy.path.abspath(filepath), scene=bpy.context.scene) + + set_orig_render_settings(ors) + + +def generate_hdr_thumbnail(): + scene = bpy.context.scene + ui_props = scene.blenderkitUI + hdr_image = ui_props.hdr_upload_image#bpy.data.images.get(ui_props.hdr_upload_image) + + base, ext = os.path.splitext(hdr_image.filepath) + thumb_path = base + '.jpg' + thumb_name = os.path.basename(thumb_path) + + max_thumbnail_size = 2048 + size = hdr_image.size + ratio = size[0] / size[1] + + imageWidth = size[0] + imageHeight = size[1] + thumbnailWidth = min(size[0], max_thumbnail_size) + thumbnailHeight = min(size[1], int(max_thumbnail_size / ratio)) + + tempBuffer = numpy.empty(imageWidth * imageHeight * 4, dtype=numpy.float32) + inew = bpy.data.images.new(thumb_name, imageWidth, imageHeight, alpha=False, float_buffer=False) + + hdr_image.pixels.foreach_get(tempBuffer) + + inew.filepath = thumb_path + inew.colorspace_settings.name = 'Linear' + inew.pixels.foreach_set(tempBuffer) + + bpy.context.view_layer.update() + if thumbnailWidth < imageWidth: + inew.scale(thumbnailWidth, thumbnailHeight) + + img_save_as(inew, filepath=inew.filepath) + + diff --git a/blenderkit/paths.py b/blenderkit/paths.py index ef0dd19d..95c5fd93 100644 --- a/blenderkit/paths.py +++ b/blenderkit/paths.py @@ -31,6 +31,8 @@ BLENDERKIT_MANUAL = "https://youtu.be/1hVgcQhIAo8" BLENDERKIT_MODEL_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/upload/" BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-material/" BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-brush/" +BLENDERKIT_HDR_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-brush/" +BLENDERKIT_SCENE_UPLOAD_INSTRUCTIONS_URL = "https://www.blenderkit.com/docs/uploading-brush/" BLENDERKIT_LOGIN_URL = "https://www.blenderkit.com/accounts/login" BLENDERKIT_OAUTH_LANDING_URL = "/oauth-landing/" BLENDERKIT_SIGNUP_URL = "https://www.blenderkit.com/accounts/register" @@ -295,6 +297,8 @@ def get_download_filepaths(asset_data, resolution='blend', can_return_others = F # utils.pprint('get download filenames ', dict(res_file)) file_names = [] + if not res_file: + return file_names # fn = asset_data['file_name'].replace('blend_', '') if res_file.get('url') is not None: #Tweak the names a bit: diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py index b6ad0185..a2ade101 100644 --- a/blenderkit/ratings.py +++ b/blenderkit/ratings.py @@ -208,6 +208,7 @@ def get_assets_for_rating(): asset_types = ( ('MODEL', 'Model', 'set of objects'), ('SCENE', 'Scene', 'scene'), + ('HDR', 'HDR', 'hdr'), ('MATERIAL', 'Material', 'any .blend Material'), ('TEXTURE', 'Texture', 'a texture, or texture set'), ('BRUSH', 'Brush', 'brush, can be any type of blender brush'), @@ -269,7 +270,7 @@ def update_quality_ui(self, context): # 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_ui = '0' self.rating_quality = int(self.rating_quality_ui) @@ -281,7 +282,7 @@ def update_ratings_work_hours_ui(self, context): # 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_ui = '0' self.rating_work_hours = float(self.rating_work_hours_ui) def update_ratings_work_hours_ui_1_5(self, context): @@ -292,7 +293,7 @@ def update_ratings_work_hours_ui_1_5(self, context): # 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' + # 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) diff --git a/blenderkit/resolutions.py b/blenderkit/resolutions.py index 89ddb6d6..a3de18ee 100644 --- a/blenderkit/resolutions.py +++ b/blenderkit/resolutions.py @@ -179,13 +179,6 @@ def save_image_safely(teximage, filepath): orig_color_mode = ims.color_mode orig_compression = ims.compression - ims = rs.image_settings - - orig_file_format = ims.file_format - orig_quality = ims.quality - orig_color_mode = ims.color_mode - orig_compression = ims.compression - ims.file_format = teximage.file_format if teximage.file_format == 'PNG': ims.color_mode = 'RGBA' @@ -317,6 +310,7 @@ def upload_resolutions(files, data): preferences = bpy.context.preferences.addons['blenderkit'].preferences upload_data = { + "name": data['asset_data']['name'], "token": preferences.api_key, "id": data['asset_data']['id'] } @@ -743,18 +737,19 @@ def iterate_for_resolutions(filepath, process_count=12, api_key=''): if check_needs_resolutions(asset_data): print('downloading and generating resolution for %s' % asset_data['name']) # this is just a quick hack for not using original dirs in blendrkit... - thread = threading.Thread(target=generate_resolution_thread, args=(asset_data, api_key)) - thread.start() - - threads.append(thread) - print('processes ', len(threads)) - while len(threads) > process_count - 1: - for proc in threads: - if not proc.is_alive(): - threads.remove(proc) - break; - else: - print(f'Failed to retrieve asset from server:{asset_data["name"]}') + generate_resolution_thread(asset_data,api_key) + # thread = threading.Thread(target=generate_resolution_thread, args=(asset_data, api_key)) + # thread.start() + # + # threads.append(thread) + # print('processes ', len(threads)) + # while len(threads) > process_count - 1: + # for t in threads: + # if not t.is_alive(): + # threads.remove(t) + # break; + # else: + # print(f'Failed to generate resolution:{asset_data["name"]}') else: print('not generated resolutions:', asset_data['name']) diff --git a/blenderkit/search.py b/blenderkit/search.py index c04d5705..f54d97d3 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -372,6 +372,9 @@ def timer_update(): if asset_type == 'scene': props = scene.blenderkit_scene # json_filepath = os.path.join(icons_dir, 'scene_searchresult.json') + if asset_type == 'hdr': + props = scene.blenderkit_HDR + # json_filepath = os.path.join(icons_dir, 'scene_searchresult.json') if asset_type == 'material': props = scene.blenderkit_mat # json_filepath = os.path.join(icons_dir, 'material_searchresult.json') @@ -1131,6 +1134,19 @@ def build_query_scene(): return query +def build_query_HDR(): + '''use all search input to request results from server''' + + props = bpy.context.scene.blenderkit_scene + query = { + "asset_type": 'hdr', + # "engine": props.search_engine, + # "adult": props.search_adult, + } + build_query_common(query, props) + return query + + def build_query_material(): props = bpy.context.scene.blenderkit_mat query = { @@ -1221,7 +1237,8 @@ def add_search_process(query, params, orig_result): while (len(search_threads) > 0): old_thread = search_threads.pop(0) old_thread[0].stop() - # TODO CARE HERE FOR ALSO KILLING THE THREADS...AT LEAST NOW SEARCH DONE FIRST WON'T REWRITE AN OLDER ONE + # 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) @@ -1304,6 +1321,12 @@ def search(category='', get_next=False, author_id=''): props = scene.blenderkit_scene query = build_query_scene() + if ui_props.asset_type == 'HDR': + if not hasattr(scene, 'blenderkit_HDR'): + return; + props = scene.blenderkit_HDR + query = build_query_HDR() + if ui_props.asset_type == 'MATERIAL': if not hasattr(scene, 'blenderkit_mat'): return; @@ -1367,7 +1390,7 @@ def search_update(self, context): if ui_props.down_up != 'SEARCH': ui_props.down_up = 'SEARCH' - # here we tweak the input if it comes form the clipboard. we need to get rid of asset type and set it to + # here we tweak the input if it comes form the clipboard. we need to get rid of asset type and set it in UI sprops = utils.get_search_props() instr = 'asset_base_id:' atstr = 'asset_type:' @@ -1376,8 +1399,6 @@ def search_update(self, context): ati = kwds.find(atstr) # if the asset type already isn't there it means this update function # was triggered by it's last iteration and needs to cancel - if idi > -1 and ati == -1: - return; if ati > -1: at = kwds[ati:].lower() # uncertain length of the remaining string - find as better method to check the presence of asset type @@ -1387,12 +1408,19 @@ def search_update(self, context): ui_props.asset_type = 'MATERIAL' elif at.find('brush') > -1: ui_props.asset_type = 'BRUSH' + elif at.find('scene') > -1: + ui_props.asset_type = 'SCENE' + elif at.find('hdr') > -1: + ui_props.asset_type = 'HDR' # now we trim the input copypaste by anything extra that is there, # this is also a way for this function to recognize that it already has parsed the clipboard # the search props can have changed and this needs to transfer the data to the other field # this complex behaviour is here for the case where the user needs to paste manually into blender? sprops = utils.get_search_props() sprops.search_keywords = kwds[:ati].rstrip() + #return here since writing into search keywords triggers this update function once more. + return + search() diff --git a/blenderkit/ui.py b/blenderkit/ui.py index fdba3971..91b8397a 100644 --- a/blenderkit/ui.py +++ b/blenderkit/ui.py @@ -61,6 +61,7 @@ reports = [] mappingdict = { 'MODEL': 'model', 'SCENE': 'scene', + 'HDR': 'hdr', 'MATERIAL': 'material', 'TEXTURE': 'texture', 'BRUSH': 'brush' @@ -699,12 +700,11 @@ def draw_callback_2d_progress(self, context): draw_downloader(loc[0], loc[1], percent=tcom.progress, img=img, text=tcom.report) else: draw_downloader(loc[0], loc[1], percent=tcom.progress, img=img, text=tcom.report) - - else: draw_progress(x, y - index * 30, text='downloading %s' % asset_data['name'], percent=tcom.progress) index += 1 + for process in bg_blender.bg_processes: tcom = process[1] draw_progress(x, y - index * 30, '%s' % tcom.lasttext, @@ -721,6 +721,11 @@ def draw_callback_2d_upload_preview(self, context): ui_props = context.scene.blenderkitUI props = utils.get_upload_props() + + #assets which don't need asset preview + if ui_props.asset_type =='HDR': + return + if props != None and ui_props.draw_tooltip: if ui_props.asset_type != 'BRUSH': @@ -1378,12 +1383,11 @@ class AssetBarOperator(bpy.types.Operator): ao = bpy.context.active_object if ui_props.asset_type == 'MODEL' and ao != None \ or ui_props.asset_type == 'MATERIAL' and ao != None and ao.active_material != None \ - or ui_props.asset_type == 'BRUSH' and utils.get_active_brush() is not None: - export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props = upload.get_upload_data( - self, - context, - ui_props.asset_type) - ui_props.tooltip = search.generate_tooltip(upload_data) + or ui_props.asset_type == 'BRUSH' and utils.get_active_brush() is not None\ + or ui_props.asset_type =='SCENE' or ui_props.asset_type == 'HDR': + export_data, upload_data = upload.get_upload_data(self, context, ui_props.asset_type) + if upload_data: + ui_props.tooltip = search.generate_tooltip(upload_data) return {'PASS_THROUGH'} @@ -1548,12 +1552,13 @@ class AssetBarOperator(bpy.types.Operator): ui_props = context.scene.blenderkitUI if event.value == 'PRESS' and ui_props.active_index > -1: + #start dragging models and materials if ui_props.asset_type == 'MODEL' or ui_props.asset_type == 'MATERIAL': # check if asset is locked and let the user know in that case asset_search_index = ui_props.active_index asset_data = sr[asset_search_index] if not asset_data.get('canDownload'): - message = "Let's support asset creators and Blender development." + message = "Let's support asset creators and Open source." link_text = 'Unlock the asset.' url = paths.get_bkit_url() + '/get-blenderkit/' + asset_data['id'] + '/?from_addon' bpy.ops.wm.blenderkit_url_dialog('INVOKE_REGION_WIN', url=url, message=message, @@ -1670,7 +1675,9 @@ class AssetBarOperator(bpy.types.Operator): if asset_search_index == -3: return {'RUNNING_MODAL'} + if asset_search_index > -3: + #picking of assets and using them if ui_props.asset_type == 'MATERIAL': if target_object != '': # position is for downloader: diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index 4cf9e6dc..9d5777a1 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -101,11 +101,16 @@ def draw_upload_common(layout, props, asset_type, context): op.url = paths.BLENDERKIT_MATERIAL_UPLOAD_INSTRUCTIONS_URL if asset_type == 'BRUSH': op.url = paths.BLENDERKIT_BRUSH_UPLOAD_INSTRUCTIONS_URL + if asset_type == 'SCENE': + op.url = paths.BLENDERKIT_SCENE_UPLOAD_INSTRUCTIONS_URL + if asset_type == 'HDR': + op.url = paths.BLENDERKIT_HDR_UPLOAD_INSTRUCTIONS_URL row = layout.row(align=True) if props.upload_state != '': utils.label_multiline(layout, text=props.upload_state, width=context.region.width) if props.uploading: + print(bpy.context.scene.name) op = layout.operator('object.kill_bg_process', text="", icon='CANCEL') op.process_source = asset_type op.process_type = 'UPLOAD' @@ -165,6 +170,38 @@ def prop_needed(layout, props, name, value, is_not_filled=''): icon = None row.prop(props, name) +def draw_panel_hdr_upload(self, context): + layout = self.layout + ui_props = bpy.context.scene.blenderkitUI + + # layout.prop_search(ui_props, "hdr_upload_image", bpy.data, "images") + layout.prop(ui_props, "hdr_upload_image") + + hdr = utils.get_active_HDR() + + + if hdr is not None: + props = hdr.blenderkit + + layout = self.layout + + draw_upload_common(layout, props, 'HDR', context) + + layout.prop(props, 'name') + layout.prop(props, 'description') + layout.prop(props, 'tags') + +def draw_panel_hdr_search(self, context): + s = context.scene + props = s.blenderkit_HDR + + layout = self.layout + row = layout.row() + row.prop(props, "search_keywords", text="", icon='VIEWZOOM') + draw_assetbar_show_hide(row, props) + layout.prop(props, "own_only") + + utils.label_multiline(layout, text=props.report) def draw_panel_model_upload(self, context): ob = bpy.context.active_object @@ -225,9 +262,9 @@ def draw_panel_scene_upload(self, context): props = s.blenderkit layout = self.layout - if bpy.app.debug_value != -1: - layout.label(text='Scene upload not Implemented') - return + # if bpy.app.debug_value != -1: + # layout.label(text='Scene upload not Implemented') + # return draw_upload_common(layout, props, 'SCENE', context) # layout = layout.column() @@ -259,6 +296,7 @@ def draw_panel_scene_upload(self, context): # elif props.thumbnail_generating_state != '': # utils.label_multiline(layout, text = props.thumbnail_generating_state) + layout.prop(props, 'is_free') layout.prop(props, 'description') layout.prop(props, 'tags') layout.prop(props, 'style') @@ -664,8 +702,8 @@ def draw_panel_brush_upload(self, context): def draw_panel_brush_search(self, context): - wm = context.scene - props = wm.blenderkit_brush + s = context.scene + props = s.blenderkit_brush layout = self.layout row = layout.row() @@ -916,8 +954,9 @@ class VIEW3D_PT_blenderkit_unified(Panel): row = layout.row(align=True) row.scale_x = 1.6 row.scale_y = 1.6 - # split = row.split(factor=.5) - row.prop(ui_props, 'asset_type', expand=True, icon_only=False) + # split = row.split(factor=. + col = layout.column() + col.prop(ui_props, 'asset_type', expand=True, icon_only=False) # row = layout.column(align = False) # layout.prop(ui_props, 'asset_type', expand=False, text='') @@ -954,7 +993,9 @@ class VIEW3D_PT_blenderkit_unified(Panel): if ui_props.asset_type == 'SCENE': # noinspection PyCallByClass draw_panel_scene_search(self, context) - + if ui_props.asset_type == 'HDR': + # noinspection PyCallByClass + draw_panel_hdr_search(self, context) elif ui_props.asset_type == 'MATERIAL': draw_panel_material_search(self, context) elif ui_props.asset_type == 'BRUSH': @@ -962,7 +1003,7 @@ class VIEW3D_PT_blenderkit_unified(Panel): # noinspection PyCallByClass draw_panel_brush_search(self, context) else: - utils.label_multiline(layout, text='switch to paint or sculpt mode.', width=context.region.width) + utils.label_multiline(layout, text='Switch to paint or sculpt mode.', width=context.region.width) return @@ -991,6 +1032,8 @@ class VIEW3D_PT_blenderkit_unified(Panel): layout.label(text='selet object to upload') elif ui_props.asset_type == 'SCENE': draw_panel_scene_upload(self, context) + elif ui_props.asset_type == 'HDR': + draw_panel_hdr_upload(self, context) elif ui_props.asset_type == 'MATERIAL': # utils.label_multiline(layout, "Uploaded materials won't be available in b2.79", icon='ERROR') @@ -1004,7 +1047,7 @@ class VIEW3D_PT_blenderkit_unified(Panel): if context.sculpt_object or context.image_paint_object: draw_panel_brush_upload(self, context) else: - layout.label(text='switch to paint or sculpt mode.') + layout.label(text='Switch to paint or sculpt mode.') elif ui_props.down_up == 'RATING': # the poll functions didn't work here, don't know why. @@ -1159,6 +1202,7 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False): op.model_rotation = o.rotation_euler op.target_object = o.name op.material_target_slot = o.active_material_index + elif asset_data['assetType'] == 'MATERIAL': aob = bpy.context.active_object op.model_location = aob.location @@ -1166,6 +1210,8 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False): op.target_object = aob.name op.material_target_slot = aob.active_material_index op.replace_resolution = True + op.replace = False + op.invoke_resolution = True op.max_resolution = asset_data.get('max_resolution', 0) # str(utils.get_param(asset_data, 'textureResolutionMax')) @@ -1177,6 +1223,7 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False): op.asset_index = ui_props.active_index # op.asset_type = ui_props.asset_type op.replace_resolution = True + op.replace = False op.invoke_resolution = True o = utils.get_active_model() if o and o.get('asset_data'): @@ -1514,11 +1561,15 @@ def header_search_draw(self, context): props = s.blenderkit_mat if ui_props.asset_type == 'BRUSH': props = s.blenderkit_brush + if ui_props.asset_type == 'HDR': + props = s.blenderkit_HDR + if ui_props.asset_type == 'SCENE': + props = s.blenderkit_scene # the center snap menu is in edit and object mode if tool settings are off. if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'): layout.separator_spacer() - layout.prop(ui_props, "asset_type", text='', icon='URL') + layout.prop(ui_props, "asset_type", expand = True, icon_only = True, text='', icon='URL') layout.prop(props, "search_keywords", text="", icon='VIEWZOOM') draw_assetbar_show_hide(layout, props) diff --git a/blenderkit/upload.py b/blenderkit/upload.py index f99b4d56..89fa50d6 100644 --- a/blenderkit/upload.py +++ b/blenderkit/upload.py @@ -33,15 +33,19 @@ if "bpy" in locals(): colors = reload(colors) rerequests = reload(rerequests) categories = reload(categories) + upload_bg = reload(upload_bg) + tasks_queue = reload(tasks_queue) + image_utils = reload(image_utils) else: from blenderkit import asset_inspector, paths, utils, bg_blender, autothumb, version_checker, search, ui_panels, ui, \ - overrides, colors, rerequests, categories + overrides, colors, rerequests, categories, upload_bg, tasks_queue, image_utils import tempfile, os, subprocess, json, re import bpy import requests import threading +import sys BLENDERKIT_EXPORT_DATA_FILE = "data.json" @@ -82,13 +86,11 @@ def add_version(data): data["addonVersion"] = addon_version - - def write_to_report(props, text): props.report = props.report + text + '\n' -def get_missing_data_model(props): +def check_missing_data_model(props): props.report = '' autothumb.update_upload_model_preview(None, None) @@ -106,7 +108,7 @@ def get_missing_data_model(props): write_to_report(props, 'Run autotags operator or fill in dimensions manually') -def get_missing_data_scene(props): +def check_missing_data_scene(props): props.report = '' autothumb.update_upload_model_preview(None, None) @@ -122,7 +124,7 @@ def get_missing_data_scene(props): write_to_report(props, 'Set at least one rendering/output engine') -def get_missing_data_material(props): +def check_missing_data_material(props): props.report = '' autothumb.update_upload_material_preview(None, None) if props.name == '': @@ -136,7 +138,7 @@ def get_missing_data_material(props): write_to_report(props, 'Set rendering/output engine') -def get_missing_data_brush(props): +def check_missing_data_brush(props): autothumb.update_upload_brush_preview(None, None) props.report = '' if props.name == '': @@ -160,11 +162,31 @@ def camel_to_sub(content): def get_upload_data(self, context, asset_type): + ''' + works though metadata from addom props and prepares it for upload to dicts. + Parameters + ---------- + self + context + asset_type + + Returns + ------- + export_ddta- all extra data that the process needs to upload and communicate with UI from a thread. + - eval_path_computing - string path to UI prop that denots if upload is still running + - eval_path_state - string path to UI prop that delivers messages about upload to ui + - eval_path - path to object holding upload data to be able to access it with various further commands + - models - in case of model upload, list of objects + - thumbnail_path - path to thumbnail file + + upload_data - asset_data generated from the ui properties + + ''' user_preferences = bpy.context.preferences.addons['blenderkit'].preferences api_key = user_preferences.api_key export_data = { - "type": asset_type, + # "type": asset_type, } upload_params = {} if asset_type == 'MODEL': @@ -236,7 +258,7 @@ def get_upload_data(self, context, asset_type): "procedural": props.is_procedural, "nodeCount": props.node_count, "textureCount": props.texture_count, - "megapixels": round(props.total_megapixels/ 1000000), + "megapixels": round(props.total_megapixels / 1000000), # "scene": props.is_scene, } if props.use_design_year: @@ -364,7 +386,7 @@ def get_upload_data(self, context, asset_type): "procedural": props.is_procedural, "nodeCount": props.node_count, "textureCount": props.texture_count, - "megapixels": round(props.total_megapixels/ 1000000), + "megapixels": round(props.total_megapixels / 1000000), } @@ -405,6 +427,38 @@ def get_upload_data(self, context, asset_type): "assetType": 'brush', } + elif asset_type == 'HDR': + ui_props = bpy.context.scene.blenderkitUI + + # imagename = ui_props.hdr_upload_image + image = ui_props.hdr_upload_image#bpy.data.images.get(imagename) + if not image: + return None, None + + props = image.blenderkit + # props.name = brush.name + base, ext = os.path.splitext(image.filepath) + thumb_path = base + '.jpg' + export_data["thumbnail_path"] = bpy.path.abspath(thumb_path) + + export_data["hdr"] = str(image.name) + export_data["hdr_filepath"] = str(bpy.path.abspath(image.filepath)) + # export_data["thumbnail_path"] = bpy.path.abspath(brush.icon_filepath) + + eval_path_computing = "bpy.data.images['%s'].blenderkit.uploading" % image.name + eval_path_state = "bpy.data.images['%s'].blenderkit.upload_state" % image.name + eval_path = "bpy.data.images['%s']" % image.name + + # mat analytics happen here, since they don't take up any time... + + upload_params = { + + } + + upload_data = { + "assetType": 'hdr', + } + elif asset_type == 'TEXTURE': style = props.style # if style == 'OTHER': @@ -441,13 +495,16 @@ def get_upload_data(self, context, asset_type): upload_data["isPrivate"] = props.is_private == 'PRIVATE' upload_data["token"] = user_preferences.api_key - if props.asset_base_id != '': - upload_data['assetBaseId'] = props.asset_base_id - upload_data['id'] = props.id - upload_data['parameters'] = upload_params - return export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props + # if props.asset_base_id != '': + export_data['assetBaseId'] = props.asset_base_id + export_data['id'] = props.id + export_data['eval_path_computing'] = eval_path_computing + export_data['eval_path_state'] = eval_path_state + export_data['eval_path'] = eval_path + + return export_data, upload_data def category_change_thread(asset_id, category, api_key): @@ -464,7 +521,6 @@ def category_change_thread(asset_id, category, api_key): return {'FINISHED'} - # class OBJECT_MT_blenderkit_fast_category_menu(bpy.types.Menu): # bl_label = "Fast category change" # bl_idname = "OBJECT_MT_blenderkit_fast_category_menu" @@ -527,7 +583,7 @@ class FastCategory(bpy.types.Operator): # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0) # col.prop(self, 'category') - layout.prop(self, 'category')#, expand = True) + layout.prop(self, 'category') # , expand = True) props = bpy.context.scene.blenderkitUI if props.asset_type == 'MODEL': # by now block this for other asset types. # col = row.column() @@ -568,6 +624,7 @@ class FastCategory(bpy.types.Operator): wm = context.window_manager return wm.invoke_props_dialog(self) + def verification_status_change_thread(asset_id, state, api_key): upload_data = { "verificationStatus": state @@ -583,6 +640,16 @@ def verification_status_change_thread(asset_id, state, api_key): def get_upload_location(props): + ''' + not used by now, gets location of uploaded asset - potentially usefull if we draw a nice upload gizmo in viewport. + Parameters + ---------- + props + + Returns + ------- + + ''' scene = bpy.context.scene ui_props = scene.blenderkitUI if ui_props.asset_type == 'MODEL': @@ -630,8 +697,244 @@ def auto_fix(asset_type=''): asset.name = props.name +upload_threads = [] + + +class Uploader(threading.Thread): + ''' + Upload thread - + - first uploads metadata + - blender gets started to process the file if .blend is uploaded + - if files need to be uploaded, uploads them + - thumbnail goes first + - files get uploaded + + Returns + ------- + + ''' + + def __init__(self, upload_data=None, export_data=None, upload_set=None): + super(Uploader, self).__init__() + self.upload_data = upload_data + self.export_data = export_data + self.upload_set = upload_set + self._stop_event = threading.Event() + + def stop(self): + self._stop_event.set() + + def stopped(self): + return self._stop_event.is_set() + + def send_message(self, message): + message = str(message) + # this adds a UI report but also writes above the upload panel fields. + tasks_queue.add_task((ui.add_report, (message,))) + estring = f"{self.export_data['eval_path_state']} = '{message}'" + tasks_queue.add_task((exec, (estring,))) + + def end_upload(self, message): + estring = self.export_data['eval_path_computing'] + ' = False' + tasks_queue.add_task((exec, (estring,))) + self.send_message(message) + + def run(self): + # utils.pprint(upload_data) + self.upload_data['parameters'] = utils.dict_to_params( + self.upload_data['parameters']) # weird array conversion only for upload, not for tooltips. + + script_path = os.path.dirname(os.path.realpath(__file__)) + + + # first upload metadata to server, so it can be saved inside the current file + url = paths.get_api_url() + 'assets/' + + headers = utils.get_headers(self.upload_data['token']) + + # self.upload_data['license'] = 'ovejajojo' + json_metadata = self.upload_data # json.dumps(self.upload_data, ensure_ascii=False).encode('utf8') + + # tasks_queue.add_task((ui.add_report, ('Posting metadata',))) + self.send_message('Posting metadata') + if self.export_data['assetBaseId'] == '': + try: + r = rerequests.post(url, json=json_metadata, headers=headers, verify=True, + immediate=True) # files = files, + + # tasks_queue.add_task((ui.add_report, ('uploaded metadata',))) + utils.p(r.text) + self.send_message('uploaded metadata') + + except requests.exceptions.RequestException as e: + print(e) + self.end_upload(e) + return {'CANCELLED'} + + else: + url += self.export_data['id'] + '/' + try: + if 'MAINFILE' in self.upload_set: + json_metadata["verificationStatus"] = "uploading" + r = rerequests.patch(url, json=json_metadata, headers=headers, verify=True, + immediate=True) # files = files, + self.send_message('uploaded metadata') + + # tasks_queue.add_task((ui.add_report, ('uploaded metadata',))) + # parse the request + # print('uploaded metadata') + print(r.text) + except requests.exceptions.RequestException as e: + print(e) + self.end_upload(e) + return {'CANCELLED'} + + if self.stopped(): + self.end_upload('Upload cancelled by user') + return + # props.upload_state = 'step 1' + if self.upload_set == ['METADATA']: + self.end_upload('Metadata posted successfully') + return {'FINISHED'} + try: + rj = r.json() + utils.pprint(rj) + # if r.status_code not in (200, 201): + # if r.status_code == 401: + # ###ui.add_report(r.detail, 5, colors.RED) + # return {'CANCELLED'} + # if props.asset_base_id == '': + # props.asset_base_id = rj['assetBaseId'] + # props.id = rj['id'] + if self.export_data['assetBaseId'] == '': + self.export_data['assetBaseId'] = rj['assetBaseId'] + self.export_data['id'] = rj['id'] + #here we need to send asset ID's back into UI to be written in asset data. + estring = f"{self.export_data['eval_path']}.blenderkit.asset_base_id = '{rj['assetBaseId']}'" + tasks_queue.add_task((exec, (estring,))) + estring = f"{self.export_data['eval_path']}.blenderkit.id = '{rj['id']}'" + tasks_queue.add_task((exec, (estring,))) + #after that, the user's file needs to be saved to save the + + self.upload_data['assetBaseId'] = self.export_data['assetBaseId'] + self.upload_data['id'] = self.export_data['id'] + + + # props.uploading = True + + if 'MAINFILE' in self.upload_set: + if self.upload_data['assetType'] == 'hdr': + fpath = self.export_data['hdr_filepath'] + else: + fpath = os.path.join(self.export_data['temp_dir'], self.upload_data['assetBaseId'] + '.blend') + + clean_file_path = paths.get_clean_filepath() + + data = { + 'export_data': self.export_data, + 'upload_data': self.upload_data, + 'debug_value': self.export_data['debug_value'], + 'upload_set': self.upload_set, + } + datafile = os.path.join(self.export_data['temp_dir'], BLENDERKIT_EXPORT_DATA_FILE) + + with open(datafile, 'w') as s: + json.dump(data, s) + + #non waiting method - not useful here.. + # proc = subprocess.Popen([ + # binary_path, + # "--background", + # "-noaudio", + # clean_file_path, + # "--python", os.path.join(script_path, "upload_bg.py"), + # "--", datafile # ,filepath, tempdir + # ], bufsize=5000, stdout=subprocess.PIPE, stdin=subprocess.PIPE) + # tasks_queue.add_task((ui.add_report, ('preparing scene - running blender instance',))) + self.send_message('preparing scene - running blender instance') + + proc = subprocess.run([ + self.export_data['binary_path'], + "--background", + "-noaudio", + clean_file_path, + "--python", os.path.join(script_path, "upload_bg.py"), + "--", datafile + ], bufsize=1, stdout=sys.stdout, stdin=subprocess.PIPE, creationflags=utils.get_process_flags()) + + if self.stopped(): + self.end_upload('Upload stopped by user') + return + + + files = [] + if 'THUMBNAIL' in self.upload_set: + files.append({ + "type": "thumbnail", + "index": 0, + "file_path": self.export_data["thumbnail_path"] + }) + if 'MAINFILE' in self.upload_set: + + files.append({ + "type": "blend", + "index": 0, + "file_path": fpath + }) + + self.send_message('Uploading files') + + uploaded = upload_bg.upload_files(self.upload_data, files) + + if uploaded: + # mark on server as uploaded + if 'MAINFILE' in self.upload_set: + confirm_data = { + "verificationStatus": "uploaded" + } + + url = paths.get_api_url() + 'assets/' + + headers = utils.get_headers(self.upload_data['token']) + + url += self.upload_data["id"] + '/' + + r = rerequests.patch(url, json=confirm_data, headers=headers, verify=True) # files = files, + + self.end_upload('Upload finished successfully') + else: + self.end_upload('Upload failed') + except Exception as e: + self.end_upload(e) + print(e) + return {'CANCELLED'} + + +def check_missing_data(asset_type, props): + ''' + checks if user did everything allright for particular assets and notifies him back if not. + Parameters + ---------- + asset_type + props + + Returns + ------- + + ''' + if asset_type == 'MODEL': + check_missing_data_model(props) + if asset_type == 'SCENE': + check_missing_data_scene(props) + elif asset_type == 'MATERIAL': + check_missing_data_material(props) + elif asset_type == 'BRUSH': + check_missing_data_brush(props) + + + def start_upload(self, context, asset_type, reupload, upload_set): - '''start upload process, by processing data''' + '''start upload process, by processing data, then start a thread that cares about the rest of the upload.''' # fix the name first utils.name_update() @@ -651,17 +954,10 @@ def start_upload(self, context, asset_type, reupload, upload_set): props.tags = props.tags[:] props.name = props.name.strip() - # TODO move this to separate function - # check for missing metadata - if asset_type == 'MODEL': - get_missing_data_model(props) - if asset_type == 'SCENE': - get_missing_data_scene(props) - elif asset_type == 'MATERIAL': - get_missing_data_material(props) - elif asset_type == 'BRUSH': - get_missing_data_brush(props) + # check for missing metadata + check_missing_data(asset_type, props) + # if previous check did find any problems then if props.report != '': self.report({'ERROR_INVALID_INPUT'}, props.report) return {'CANCELLED'} @@ -669,131 +965,50 @@ def start_upload(self, context, asset_type, reupload, upload_set): if not reupload: props.asset_base_id = '' props.id = '' - export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props = get_upload_data(self, context, - asset_type) - # utils.pprint(upload_data) - upload_data['parameters'] = utils.dict_to_params( - upload_data['parameters']) # weird array conversion only for upload, not for tooltips. - - binary_path = bpy.app.binary_path - script_path = os.path.dirname(os.path.realpath(__file__)) - basename, ext = os.path.splitext(bpy.data.filepath) - # if not basename: - # basename = os.path.join(basename, "temp") - if not ext: - ext = ".blend" - tempdir = tempfile.mkdtemp() - source_filepath = os.path.join(tempdir, "export_blenderkit" + ext) - clean_file_path = paths.get_clean_filepath() - data = { - 'clean_file_path': clean_file_path, - 'source_filepath': source_filepath, - 'temp_dir': tempdir, - 'export_data': export_data, - 'upload_data': upload_data, - 'debug_value': bpy.app.debug_value, - 'upload_set': upload_set, - } - datafile = os.path.join(tempdir, BLENDERKIT_EXPORT_DATA_FILE) - # check if thumbnail exists: + export_data, upload_data = get_upload_data(self, context, asset_type) + + # check if thumbnail exists, generate for HDR: if 'THUMBNAIL' in upload_set: - if not os.path.exists(export_data["thumbnail_path"]): + if asset_type == 'HDR': + image_utils.generate_hdr_thumbnail() + elif not os.path.exists(export_data["thumbnail_path"]): props.upload_state = 'Thumbnail not found' props.uploading = False return {'CANCELLED'} - # first upload metadata to server, so it can be saved inside the current file - url = paths.get_api_url() + 'assets/' + props.upload_state = "Starting upload. Please don't close Blender until upload finishes" + props.uploading = True - headers = utils.get_headers(upload_data['token']) - # upload_data['license'] = 'ovejajojo' - json_metadata = upload_data # json.dumps(upload_data, ensure_ascii=False).encode('utf8') - global reports - if props.asset_base_id == '': - try: - r = rerequests.post(url, json=json_metadata, headers=headers, verify=True, immediate=True) # files = files, - ui.add_report('uploaded metadata') - utils.p(r.text) - except requests.exceptions.RequestException as e: - print(e) - props.upload_state = str(e) - props.uploading = False - return {'CANCELLED'} + # save a copy of the file for processing. Only for blend files + basename, ext = os.path.splitext(bpy.data.filepath) + if not ext: + ext = ".blend" + export_data['temp_dir'] = tempfile.mkdtemp() + export_data['source_filepath'] = os.path.join(export_data['temp_dir'], "export_blenderkit" + ext) + if asset_type != 'HDR': + bpy.ops.wm.save_as_mainfile(filepath=export_data['source_filepath'], compress=False, copy=True) - else: - url += props.id + '/' - try: - if 'MAINFILE' in upload_set: - json_metadata["verificationStatus"] = "uploading" - r = rerequests.patch(url, json=json_metadata, headers=headers, verify=True, immediate=True) # files = files, - ui.add_report('uploaded metadata') - # parse the request - # print('uploaded metadata') - print(r.text) - except requests.exceptions.RequestException as e: - print(e) - props.upload_state = str(e) - props.uploading = False - return {'CANCELLED'} + export_data['binary_path'] = bpy.app.binary_path + export_data['debug_value'] = bpy.app.debug_value - # props.upload_state = 'step 1' - if upload_set == ['METADATA']: - props.uploading = False - props.upload_state = 'upload finished successfully' - return {'FINISHED'} - try: - rj = r.json() - utils.pprint(rj) - # if r.status_code not in (200, 201): - # if r.status_code == 401: - # ui.add_report(r.detail, 5, colors.RED) - # return {'CANCELLED'} - if props.asset_base_id == '': - props.asset_base_id = rj['assetBaseId'] - props.id = rj['id'] - upload_data['assetBaseId'] = props.asset_base_id - upload_data['id'] = props.id - - # bpy.ops.wm.save_mainfile() - # bpy.ops.wm.save_as_mainfile(filepath=filepath, compress=False, copy=True) - - props.uploading = True - # save a copy of actual scene but don't interfere with the users models - bpy.ops.wm.save_as_mainfile(filepath=source_filepath, compress=False, copy=True) - - with open(datafile, 'w') as s: - json.dump(data, s) - - proc = subprocess.Popen([ - binary_path, - "--background", - "-noaudio", - clean_file_path, - "--python", os.path.join(script_path, "upload_bg.py"), - "--", datafile # ,filepath, tempdir - ], bufsize=5000, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - - bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state, - eval_path=eval_path, process_type='UPLOAD', process=proc, location=location) - - except Exception as e: - props.upload_state = str(e) - props.uploading = False - print(e) - return {'CANCELLED'} + upload_thread = Uploader(upload_data=upload_data, export_data=export_data, upload_set=upload_set) + + upload_thread.start() + upload_threads.append(upload_thread) return {'FINISHED'} asset_types = ( - ('MODEL', 'Model', 'set of objects'), - ('SCENE', 'Scene', 'scene'), - ('MATERIAL', 'Material', 'any .blend Material'), - ('TEXTURE', 'Texture', 'a texture, or texture set'), - ('BRUSH', 'Brush', 'brush, can be any type of blender brush'), - ('ADDON', 'Addon', 'addnon'), + ('MODEL', 'Model', 'Set of objects'), + ('SCENE', 'Scene', 'Scene'), + ('HDR', 'HDR', 'HDR image'), + ('MATERIAL', 'Material', 'Any .blend Material'), + ('TEXTURE', 'Texture', 'A texture, or texture set'), + ('BRUSH', 'Brush', 'Brush, can be any type of blender brush'), + ('ADDON', 'Addon', 'Addnon'), ) @@ -865,11 +1080,9 @@ class UploadOperator(Operator): if self.main_file: upload_set.append('MAINFILE') - result = start_upload(self, context, self.asset_type, self.reupload, upload_set) - - return result - + result = start_upload(self, context, self.asset_type, self.reupload, upload_set=upload_set, ) + return {'FINISHED'} def draw(self, context): props = utils.get_upload_props() @@ -887,16 +1100,16 @@ class UploadOperator(Operator): if props.is_private == 'PUBLIC': utils.label_multiline(layout, text='public assets are validated several hours' - ' or days after upload. Remember always to ' - 'test download your asset to a clean file' - ' to see if it uploaded correctly.' - , width=300) + ' or days after upload. Remember always to ' + 'test download your asset to a clean file' + ' to see if it uploaded correctly.' + , width=300) def invoke(self, context, event): props = utils.get_upload_props() if not utils.user_logged_in(): - ui_panels.draw_not_logged_in(self, message = 'To upload assets you need to login/signup.') + ui_panels.draw_not_logged_in(self, message='To upload assets you need to login/signup.') return {'CANCELLED'} if props.is_private == 'PUBLIC': @@ -905,7 +1118,6 @@ class UploadOperator(Operator): return self.execute(context) - class AssetDebugPrint(Operator): """Change verification status""" bl_idname = "object.blenderkit_print_asset_debug" @@ -922,7 +1134,6 @@ class AssetDebugPrint(Operator): def poll(cls, context): return True - def execute(self, context): preferences = bpy.context.preferences.addons['blenderkit'].preferences @@ -993,7 +1204,6 @@ class AssetVerificationStatusChange(Operator): if r['id'] == self.asset_id: r['verificationStatus'] = self.state - thread = threading.Thread(target=verification_status_change_thread, args=(self.asset_id, self.state, preferences.api_key)) thread.start() diff --git a/blenderkit/upload_bg.py b/blenderkit/upload_bg.py index a0e95535..74ae8906 100644 --- a/blenderkit/upload_bg.py +++ b/blenderkit/upload_bg.py @@ -25,8 +25,10 @@ if "bpy" in locals(): bg_blender = reload(bg_blender) utils = reload(utils) rerequests = reload(rerequests) + tasks_queue = reload(tasks_queue) + ui = reload(ui) else: - from blenderkit import paths, append_link, bg_blender, utils, rerequests + from blenderkit import paths, append_link, bg_blender, utils, rerequests, tasks_queue, ui import sys, json, os, time import requests @@ -66,7 +68,9 @@ class upload_in_chunks(object): break self.readsofar += len(data) percent = self.readsofar * 1e2 / self.totalsize - bg_blender.progress('uploading %s' % self.report_name, percent) + tasks_queue.add_task((ui.add_report, (f"Uploading {self.report_name} {percent}%",))) + + # bg_blender.progress('uploading %s' % self.report_name, percent) # sys.stderr.write("\r{percent:3.0f}%".format(percent=percent)) yield data @@ -77,7 +81,10 @@ class upload_in_chunks(object): def upload_file(upload_data, f): headers = utils.get_headers(upload_data['token']) version_id = upload_data['id'] - bg_blender.progress(f"uploading {f['type']} {os.path.basename(f['file_path'])}") + + message = f"uploading {f['type']} {os.path.basename(f['file_path'])}" + tasks_queue.add_task((ui.add_report, (message,))) + upload_info = { 'assetId': version_id, 'fileType': f['type'], @@ -100,14 +107,17 @@ def upload_file(upload_data, f): data=upload_in_chunks(f['file_path'], chunk_size, f['type']), stream=True, verify=True) - if 250>upload_response.status_code >199: + if 250 > upload_response.status_code > 199: uploaded = True else: print(upload_response.text) - bg_blender.progress(f'Upload failed, retry. {a}') + message = f"Upload failed, retry. File : {f['type']} {os.path.basename(f['file_path'])}" + tasks_queue.add_task((ui.add_report, (message,))) + except Exception as e: print(e) - bg_blender.progress('Upload %s failed, retrying' % f['type']) + message = f"Upload failed, retry. File : {f['type']} {os.path.basename(f['file_path'])}" + tasks_queue.add_task((ui.add_report, (message,))) time.sleep(1) # confirm single file upload to bkit server @@ -115,7 +125,7 @@ def upload_file(upload_data, f): upload_done_url = paths.get_api_url() + 'uploads_s3/' + upload['id'] + '/upload-file/' upload_response = rerequests.post(upload_done_url, headers=headers, verify=True) - bg_blender.progress('finished uploading') + tasks_queue.add_task((ui.add_report, (f"Finished file upload{os.path.basename(f['file_path'])}",))) return uploaded @@ -127,13 +137,13 @@ def upload_files(upload_data, files): uploaded = upload_file(upload_data, f) if not uploaded: uploaded_all = False - bg_blender.progress('finished uploading') + tasks_queue.add_task((ui.add_report, (f"Uploaded all files for asset {upload_data['name']}",))) return uploaded_all if __name__ == "__main__": try: - bg_blender.progress('preparing scene - append data') + # bg_blender.progress('preparing scene - append data') with open(BLENDERKIT_EXPORT_DATA, 'r') as s: data = json.load(s) @@ -141,84 +151,44 @@ if __name__ == "__main__": export_data = data['export_data'] upload_data = data['upload_data'] - upload_set = data['upload_set'] - if 'MAINFILE' in upload_set: - bpy.data.scenes.new('upload') - for s in bpy.data.scenes: - if s.name != 'upload': - bpy.data.scenes.remove(s) - - if export_data['type'] == 'MODEL': - obnames = export_data['models'] - main_source, allobs = append_link.append_objects(file_name=data['source_filepath'], - obnames=obnames, - rotation=(0, 0, 0)) - g = bpy.data.collections.new(upload_data['name']) - for o in allobs: - g.objects.link(o) - bpy.context.scene.collection.children.link(g) - if export_data['type'] == 'SCENE': - sname = export_data['scene'] - main_source = append_link.append_scene(file_name=data['source_filepath'], - scenename=sname) - bpy.data.scenes.remove(bpy.data.scenes['upload']) - main_source.name = sname - elif export_data['type'] == 'MATERIAL': - matname = export_data['material'] - main_source = append_link.append_material(file_name=data['source_filepath'], matname=matname) - - elif export_data['type'] == 'BRUSH': - brushname = export_data['brush'] - main_source = append_link.append_brush(file_name=data['source_filepath'], brushname=brushname) - - bpy.ops.file.pack_all() - - main_source.blenderkit.uploading = False - fpath = os.path.join(data['temp_dir'], upload_data['assetBaseId'] + '.blend') - - bpy.ops.wm.save_as_mainfile(filepath=fpath, compress=True, copy=False) - os.remove(data['source_filepath']) - - bg_blender.progress('preparing scene - open files') - - files = [] - if 'THUMBNAIL' in upload_set: - files.append({ - "type": "thumbnail", - "index": 0, - "file_path": export_data["thumbnail_path"] - }) - if 'MAINFILE' in upload_set: - files.append({ - "type": "blend", - "index": 0, - "file_path": fpath - }) - - bg_blender.progress('uploading') - - uploaded = upload_files(upload_data, files) - - if uploaded: - # mark on server as uploaded - if 'MAINFILE' in upload_set: - confirm_data = { - "verificationStatus": "uploaded" - } - - url = paths.get_api_url() + 'assets/' - - headers = utils.get_headers(upload_data['token']) - - url += upload_data["id"] + '/' - - r = rerequests.patch(url, json=confirm_data, headers=headers, verify=True) # files = files, - - bg_blender.progress('upload finished successfully') - else: - bg_blender.progress('upload failed.') + bpy.data.scenes.new('upload') + for s in bpy.data.scenes: + if s.name != 'upload': + bpy.data.scenes.remove(s) + + if upload_data['assetType'] == 'model': + obnames = export_data['models'] + main_source, allobs = append_link.append_objects(file_name=export_data['source_filepath'], + obnames=obnames, + rotation=(0, 0, 0)) + g = bpy.data.collections.new(upload_data['name']) + for o in allobs: + g.objects.link(o) + bpy.context.scene.collection.children.link(g) + elif upload_data['assetType'] == 'scene': + sname = export_data['scene'] + main_source = append_link.append_scene(file_name=export_data['source_filepath'], + scenename=sname) + bpy.data.scenes.remove(bpy.data.scenes['upload']) + main_source.name = sname + elif upload_data['assetType'] == 'material': + matname = export_data['material'] + main_source = append_link.append_material(file_name=export_data['source_filepath'], matname=matname) + + elif upload_data['assetType'] == 'brush': + brushname = export_data['brush'] + main_source = append_link.append_brush(file_name=export_data['source_filepath'], brushname=brushname) + + bpy.ops.file.pack_all() + + main_source.blenderkit.uploading = False + fpath = os.path.join(export_data['temp_dir'], upload_data['assetBaseId'] + '.blend') + + bpy.ops.wm.save_as_mainfile(filepath=fpath, compress=True, copy=False) + os.remove(export_data['source_filepath']) + except Exception as e: print(e) - bg_blender.progress(e) + # bg_blender.progress(e) sys.exit(1) diff --git a/blenderkit/utils.py b/blenderkit/utils.py index 36bf1fc9..b3f65042 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -76,6 +76,13 @@ def get_active_model(): return None +def get_active_HDR(): + scene = bpy.context.scene + ui_props = scene.blenderkitUI + image = ui_props.hdr_upload_image + return image + + def get_selected_models(): ''' Detect all hierarchies that contain asset data from selection. Only parents that have actual ['asset data'] get returned @@ -150,6 +157,10 @@ def get_search_props(): if not hasattr(scene, 'blenderkit_scene'): return; props = scene.blenderkit_scene + if uiprops.asset_type == 'HDR': + if not hasattr(scene, 'blenderkit_HDR'): + return; + props = scene.blenderkit_HDR if uiprops.asset_type == 'MATERIAL': if not hasattr(scene, 'blenderkit_mat'): return; @@ -176,7 +187,8 @@ def get_active_asset(): return ob if ui_props.asset_type == 'SCENE': return bpy.context.scene - + if ui_props.asset_type == 'HDR': + return get_active_HDR() elif ui_props.asset_type == 'MATERIAL': if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None: return bpy.context.active_object.active_material @@ -199,6 +211,12 @@ def get_upload_props(): if ui_props.asset_type == 'SCENE': s = bpy.context.scene return s.blenderkit + if ui_props.asset_type == 'HDR': + + hdr = ui_props.hdr_upload_image#bpy.data.images.get(ui_props.hdr_upload_image) + if not hdr: + return None + return hdr.blenderkit elif ui_props.asset_type == 'MATERIAL': if bpy.context.view_layer.objects.active is not None and bpy.context.active_object.active_material is not None: return bpy.context.active_object.active_material.blenderkit @@ -412,6 +430,7 @@ def delete_hierarchy(ob): obs = get_hierarchy(ob) bpy.ops.object.delete({"selected_objects": obs}) + def get_bounds_snappable(obs, use_modifiers=False): # progress('getting bounds of object(s)') parent = obs[0] @@ -601,6 +620,9 @@ def automap(target_object=None, target_slot=None, tex_size=1, bg_exception=False def name_update(): + scene = bpy.context.scene + ui_props = scene.blenderkitUI + props = get_upload_props() if props.name_old != props.name: props.name_changed = True @@ -617,7 +639,9 @@ def name_update(): fname = fname.replace('\'', '') fname = fname.replace('\"', '') asset = get_active_asset() - asset.name = fname + if ui_props.asset_type != 'HDR': + # Here we actually rename assets datablocks, but don't do that with HDR's and possibly with others + asset.name = fname def get_param(asset_data, parameter_name): |