diff options
author | Peter Kim <pk15950@gmail.com> | 2021-10-03 06:16:58 +0300 |
---|---|---|
committer | Peter Kim <pk15950@gmail.com> | 2021-10-03 06:16:58 +0300 |
commit | c64726810ba781d980921947ba819b1364689e53 (patch) | |
tree | ebab21045e0ecef92475c55a6fb072435661a3fd | |
parent | a85360cbdfbbee2bb46bcb93900f597a989bd33b (diff) | |
parent | 13140e3947af5c1becd752e7fb2c627b3ed95d96 (diff) |
Merge branch 'master' into xr-controller-supportxr-controller-support
-rw-r--r-- | archipack/__init__.py | 4 | ||||
-rw-r--r-- | archipack/archipack_autoboolean.py | 6 | ||||
-rw-r--r-- | archipack/archipack_material.py | 2 | ||||
-rw-r--r-- | archipack/archipack_preset.py | 4 | ||||
-rw-r--r-- | blenderkit/overrides.py | 5 | ||||
-rw-r--r-- | blenderkit/resolutions.py | 25 | ||||
-rw-r--r-- | blenderkit/search.py | 9 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 48 | ||||
-rw-r--r-- | blenderkit/version_checker.py | 11 | ||||
-rw-r--r-- | io_scene_fbx/__init__.py | 2 | ||||
-rw-r--r-- | io_scene_fbx/export_fbx_bin.py | 5 | ||||
-rw-r--r-- | io_scene_fbx/import_fbx.py | 5 | ||||
-rwxr-xr-x | io_scene_gltf2/__init__.py | 2 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py | 10 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py | 2 | ||||
-rw-r--r-- | pose_library/gui.py | 53 | ||||
-rw-r--r-- | render_auto_tile_size.py | 500 |
17 files changed, 132 insertions, 561 deletions
diff --git a/archipack/__init__.py b/archipack/__init__.py index 0474cbd1..44c56b7c 100644 --- a/archipack/__init__.py +++ b/archipack/__init__.py @@ -31,8 +31,8 @@ bl_info = { 'author': 's-leger', 'license': 'GPL', 'deps': '', - 'version': (1, 2, 84), - 'blender': (2, 90, 0), + 'version': (1, 2, 85), + 'blender': (3, 0, 0), 'location': 'View3D > Sidebar > Create > Archipack', 'warning': '', 'doc_url': 'https://github.com/s-leger/archipack/wiki', diff --git a/archipack/archipack_autoboolean.py b/archipack/archipack_autoboolean.py index f5b6eaf4..3a424728 100644 --- a/archipack/archipack_autoboolean.py +++ b/archipack/archipack_autoboolean.py @@ -108,12 +108,6 @@ class ArchipackBoolManager(ArchipackCollectionManager): hole.hide_render = True hole.hide_select = True hole.select_set(state=True) - hole.cycles_visibility.camera = False - hole.cycles_visibility.diffuse = False - hole.cycles_visibility.glossy = False - hole.cycles_visibility.shadow = False - hole.cycles_visibility.scatter = False - hole.cycles_visibility.transmission = False def get_child_hole(self, o): for hole in o.children: diff --git a/archipack/archipack_material.py b/archipack/archipack_material.py index e363f304..5ac29d99 100644 --- a/archipack/archipack_material.py +++ b/archipack/archipack_material.py @@ -83,7 +83,7 @@ class MatLib(): """ try: # print("MatLib.load_mat(%s) linked:%s" % (name, link)) - with bpy.data.libraries.load(self.path, link, False) as (data_from, data_to): + with bpy.data.libraries.load(self.path, link=link, relative=False) as (data_from, data_to): data_to.materials = [name] except: pass diff --git a/archipack/archipack_preset.py b/archipack/archipack_preset.py index fe4b9307..65ca7245 100644 --- a/archipack/archipack_preset.py +++ b/archipack/archipack_preset.py @@ -34,8 +34,8 @@ from .archipack_gl import ( ThumbHandle, Screen, GlRect, GlPolyline, GlPolygon, GlText, GlHandle ) -preset_paths = bpy.utils.script_paths(subdir="presets") -addons_paths = bpy.utils.script_paths(subdir="addons") +preset_paths = [os.path.join(path, "presets") for path in bpy.utils.script_paths()] +addons_paths = [os.path.join(path, "addons") for path in bpy.utils.script_paths()] class CruxHandle(GlHandle): diff --git a/blenderkit/overrides.py b/blenderkit/overrides.py index c2934781..28c5e7f7 100644 --- a/blenderkit/overrides.py +++ b/blenderkit/overrides.py @@ -106,6 +106,9 @@ def addColorCorrectors(material): def modelProxy(): + utils.p('No proxies in Blender anymore') + return False + s = bpy.context.scene ao = bpy.context.active_object if utils.is_linked_asset(ao): @@ -128,7 +131,7 @@ def modelProxy(): new_ao.empty_display_type = 'SPHERE' new_ao.empty_display_size *= 0.1 - bpy.ops.object.proxy_make(object=rigs[0].name) + # bpy.ops.object.proxy_make(object=rigs[0].name) proxy = bpy.context.active_object bpy.context.view_layer.objects.active = ao ao.select_set(True) diff --git a/blenderkit/resolutions.py b/blenderkit/resolutions.py index 1a4af5fc..e2412786 100644 --- a/blenderkit/resolutions.py +++ b/blenderkit/resolutions.py @@ -168,6 +168,31 @@ def unpack_asset(data): # image.unpack(method='REMOVE') image.unpack(method='WRITE_ORIGINAL') + #mark asset browser asset + data_block = None + if asset_data['assetType'] == 'model': + for ob in bpy.context.scene.objects: + if ob.parent == None: + ob.asset_mark() + data_block = ob + elif asset_data['assetType'] == 'material': + for m in bpy.data.materials: + m.asset_mark() + data_block = m + elif asset_data['assetType'] == 'scene': + bpy.context.scene.asset_mark() + elif asset_data['assetType'] =='brush': + for b in bpy.data.brushes: + if b.get('asset_data') is not None: + b.asset_mark() + data_block = b + if data_block is not None: + tags = data_block.asset_data.tags + for t in tags: + tags.remove(t) + tags.new('description: ' + asset_data['description']) + tags.new('tags: ' + ','.join(asset_data['tags'])) + bpy.ops.wm.save_mainfile(compress=False) # now try to delete the .blend1 file try: diff --git a/blenderkit/search.py b/blenderkit/search.py index c386f588..e846e8bc 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -1313,6 +1313,15 @@ def get_search_simple(parameters, filepath=None, page_size=100, max_results=1000 bk_logger.info(f'retrieved {len(results)} assets from elastic search') return results +def get_single_asset(asset_base_id): + preferences = bpy.context.preferences.addons['blenderkit'].preferences + params = { + 'asset_base_id': asset_base_id + } + results = get_search_simple(params, api_key=preferences.api_key) + if len(results)>0: + return results[0] + return None def search(category='', get_next=False, author_id=''): ''' initialize searching''' diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index 25fa5927..f65833ed 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -1175,23 +1175,32 @@ class BlenderKitWelcomeOperator(bpy.types.Operator): # bpy.context.window_manager.windows[0].screen.areas[5].spaces[0].show_region_ui = False print('running search no') ui_props = bpy.context.scene.blenderkitUI - random_searches = [ - ('MATERIAL', 'ice'), - ('MODEL', 'car'), - ('MODEL', 'vase'), - ('MODEL', 'grass'), - ('MODEL', 'plant'), - ('MODEL', 'man'), - ('MATERIAL', 'metal'), - ('MATERIAL', 'wood'), - ('MATERIAL', 'floor'), - ('MATERIAL', 'bricks'), - ] - random_search = random.choice(random_searches) - ui_props.asset_type = random_search[0] - - bpy.context.window_manager.blenderkit_mat.search_keywords = '' # random_search[1] - bpy.context.window_manager.blenderkit_mat.search_keywords = '+is_free:true+score_gte:1000+order:-created' # random_search[1] + # random_searches = [ + # ('MATERIAL', 'ice'), + # ('MODEL', 'car'), + # ('MODEL', 'vase'), + # ('MODEL', 'grass'), + # ('MODEL', 'plant'), + # ('MODEL', 'man'), + # ('MATERIAL', 'metal'), + # ('MATERIAL', 'wood'), + # ('MATERIAL', 'floor'), + # ('MATERIAL', 'bricks'), + # ] + # random_search = random.choice(random_searches) + # ui_props.asset_type = random_search[0] + ui_props.asset_type = 'MODEL' + + score_limit = 450 + if ui_props.asset_type == 'MATERIAL': + props = bpy.context.window_manager.blenderkit_mat + + elif ui_props.asset_type == 'MODEL': + props = bpy.context.window_manager.blenderkit_models + score_limit = 1000 + + props.search_keywords = ''#random_search[1] + props.search_keywords += f'+is_free:true+score_gte:{score_limit}+order:-created' # random_search[1] # search.search() return {'FINISHED'} @@ -1655,8 +1664,9 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): # self.draw_asset_parameter(box, key='purePbr', pretext='Pure PBR') # self.draw_asset_parameter(box, key='productionLevel', pretext='Readiness') # self.draw_asset_parameter(box, key='condition', pretext='Condition') - self.draw_asset_parameter(box, key='material_style', pretext='Style') - self.draw_asset_parameter(box, key='model_style', pretext='Style') + if utils.profile_is_validator(): + self.draw_asset_parameter(box, key='materialStyle', pretext='Style') + self.draw_asset_parameter(box, key='modelStyle', pretext='Style') if utils.get_param(self.asset_data, 'dimensionX'): t = '%s×%s×%s m' % (utils.fmt_length(mparams['dimensionX']), diff --git a/blenderkit/version_checker.py b/blenderkit/version_checker.py index 993ff238..37aeadc4 100644 --- a/blenderkit/version_checker.py +++ b/blenderkit/version_checker.py @@ -16,18 +16,23 @@ # # ##### END GPL LICENSE BLOCK ##### - +import bpy from blenderkit import paths import requests, os, json, threading def get_addon_version(): - import blenderkit - ver = blenderkit.bl_info['version'] + # should return addon version, but since Blender 3.0 this is synced with Blender version + ver = bpy.app.version return '%i.%i.%i' % (ver[0], ver[1], ver[2]) + # import blenderkit + # ver = blenderkit.bl_info['version'] + # return '%i.%i.%i' % (ver[0], ver[1], ver[2]) + + def check_version(url, api_key, module): headers = { "accept": "application/json", diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py index 495e62d2..a9c58915 100644 --- a/io_scene_fbx/__init__.py +++ b/io_scene_fbx/__init__.py @@ -21,7 +21,7 @@ bl_info = { "name": "FBX format", "author": "Campbell Barton, Bastien Montagne, Jens Restemeier", - "version": (4, 24, 0), + "version": (4, 25, 0), "blender": (2, 90, 0), "location": "File > Import-Export", "description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions", diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index 3950ed5b..09dfaa9f 100644 --- a/io_scene_fbx/export_fbx_bin.py +++ b/io_scene_fbx/export_fbx_bin.py @@ -879,7 +879,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): if last_subsurf: elem_data_single_int32(geom, b"Smoothness", 2) # Display control mesh and smoothed - elem_data_single_int32(geom, b"BoundaryRule", 2) # Round edges like Blender + if last_subsurf.boundary_smooth == "PRESERVE_CORNERS": + elem_data_single_int32(geom, b"BoundaryRule", 2) # CreaseAll + else: + elem_data_single_int32(geom, b"BoundaryRule", 1) # CreaseEdge elem_data_single_int32(geom, b"PreviewDivisionLevels", last_subsurf.levels) elem_data_single_int32(geom, b"RenderDivisionLevels", last_subsurf.render_levels) diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py index ba11757a..a07e919e 100644 --- a/io_scene_fbx/import_fbx.py +++ b/io_scene_fbx/import_fbx.py @@ -2928,6 +2928,11 @@ def load(operator, context, filepath="", mod = parent.bl_obj.modifiers.new('subsurf', 'SUBSURF') mod.levels = preview_levels mod.render_levels = render_levels + boundary_rule = elem_prop_first(elem_find_first(fbx_sdata, b'BoundaryRule'), default=1) + if boundary_rule == 2: + mod.boundary_smooth = "PRESERVE_CORNERS" + else: + mod.boundary_smooth = "ALL" _(); del _ diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 9a89cfbe..41f09df9 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 28), + "version": (1, 7, 30), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py index df069f79..d70f4de2 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py @@ -317,7 +317,7 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec # We can ignore this keyframes # if there are some fcurve, we can keep only 2 keyframes, first and last if blender_object_if_armature is not None: - cst = all([j < 0.0001 for j in np.ptp([[k.value[i] for i in range(len(keyframes[0].value))] for k in keyframes], axis=0)]) + cst = fcurve_is_constant(keyframes) if node_channel_is_animated is True: # fcurve on this bone for this property # Keep animation, but keep only 2 keyframes if data are not changing @@ -325,10 +325,18 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec else: # bone is not animated (no fcurve) # Not keeping if not changing property return None if cst is True else keyframes + else: + # For objects, if all values are the same, we keep only first and last + cst = fcurve_is_constant(keyframes) + return [keyframes[0], keyframes[-1]] if cst is True and len(keyframes) >= 2 else keyframes + return keyframes +def fcurve_is_constant(keyframes): + return all([j < 0.0001 for j in np.ptp([[k.value[i] for i in range(len(keyframes[0].value))] for k in keyframes], axis=0)]) + def complete_key(key: Keyframe, non_keyed_values: typing.Tuple[typing.Optional[float]]): """ Complete keyframe with non keyed values diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py index 28a19c9e..13c347dc 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py @@ -225,7 +225,7 @@ def __gather_children(blender_object, blender_scene, export_settings): parent_joint = find_parent_joint(root_joints, child.parent_bone) if not parent_joint: continue - child_node = gather_node(child, None, None, None, export_settings) + child_node = gather_node(child, None, blender_scene, None, export_settings) if child_node is None: continue blender_bone = blender_object.pose.bones[parent_joint.name] diff --git a/pose_library/gui.py b/pose_library/gui.py index a2f04a22..da7b77c7 100644 --- a/pose_library/gui.py +++ b/pose_library/gui.py @@ -33,21 +33,25 @@ from bpy.types import ( from bpy_extras import asset_utils -class VIEW3D_PT_pose_library(Panel): +class PoseLibraryPanel: + @classmethod + def pose_library_panel_poll(cls, context: Context) -> bool: + return bool( + context.object + and context.object.mode == 'POSE' + ) + + @classmethod + def poll(cls, context: Context) -> bool: + return cls.pose_library_panel_poll(context); + + +class VIEW3D_PT_pose_library(PoseLibraryPanel, Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Animation" bl_label = "Pose Library" - @classmethod - def poll(cls, context: Context) -> bool: - exp_prefs = context.preferences.experimental - try: - return exp_prefs.use_asset_browser - except AttributeError: - # The 'use_asset_browser' experimental option was removed from Blender. - return True - def draw(self, context: Context) -> None: layout = self.layout @@ -124,11 +128,18 @@ def pose_library_list_item_context_menu(self: UIList, context: Context) -> None: layout.operator("asset.open_containing_blend_file") -class ASSETBROWSER_PT_pose_library_usage(asset_utils.AssetBrowserSpecificCategoryPanel, Panel): +class ASSETBROWSER_PT_pose_library_usage(PoseLibraryPanel, asset_utils.AssetBrowserPanel, Panel): bl_region_type = "TOOLS" bl_label = "Pose Library" asset_categories = {'ANIMATIONS'} + @classmethod + def poll(cls, context: Context) -> bool: + return ( + cls.pose_library_panel_poll(context) + and cls.asset_browser_panel_poll(context) + ) + def draw(self, context: Context) -> None: layout = self.layout wm = context.window_manager @@ -149,11 +160,18 @@ class ASSETBROWSER_PT_pose_library_usage(asset_utils.AssetBrowserSpecificCategor props.select = False -class ASSETBROWSER_PT_pose_library_editing(asset_utils.AssetBrowserSpecificCategoryPanel, Panel): +class ASSETBROWSER_PT_pose_library_editing(PoseLibraryPanel, asset_utils.AssetBrowserPanel, Panel): bl_region_type = "TOOL_PROPS" bl_label = "Pose Library" asset_categories = {'ANIMATIONS'} + @classmethod + def poll(cls, context: Context) -> bool: + return ( + cls.pose_library_panel_poll(context) + and cls.asset_browser_panel_poll(context) + ) + def draw(self, context: Context) -> None: layout = self.layout @@ -169,21 +187,12 @@ class ASSETBROWSER_PT_pose_library_editing(asset_utils.AssetBrowserSpecificCateg col.operator("poselib.paste_asset", icon="PASTEDOWN") -class DOPESHEET_PT_asset_panel(Panel): +class DOPESHEET_PT_asset_panel(PoseLibraryPanel, Panel): bl_space_type = "DOPESHEET_EDITOR" bl_region_type = "UI" bl_label = "Create Pose Asset" bl_category = "Pose Library" - @classmethod - def poll(cls, context: Context) -> bool: - exp_prefs = context.preferences.experimental - try: - return exp_prefs.use_asset_browser - except AttributeError: - # The 'use_asset_browser' experimental option was removed from Blender. - return True - def draw(self, context: Context) -> None: layout = self.layout col = layout.column(align=True) diff --git a/render_auto_tile_size.py b/render_auto_tile_size.py deleted file mode 100644 index 078513c6..00000000 --- a/render_auto_tile_size.py +++ /dev/null @@ -1,500 +0,0 @@ -# BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# END GPL LICENSE BLOCK ##### - -bl_info = { - "name": "Auto Tile Size", - "description": "Estimate and set the tile size that will render the fastest", - "author": "Greg Zaal", - "version": (3, 1, 3), - "blender": (2, 80, 0), - "location": "Render Settings > Performance", - "warning": "", - "doc_url": "{BLENDER_MANUAL_URL}/addons/render/auto_tile_size.html", - "category": "Render", -} - - -import bpy -from bpy.types import ( - Operator, - PropertyGroup, - ) -from bpy.props import ( - BoolProperty, - EnumProperty, - FloatVectorProperty, - IntProperty, - IntVectorProperty, - StringProperty, - PointerProperty, - ) -from bpy.app.handlers import persistent -from math import ( - ceil, floor, - sqrt, - ) - - -SUPPORTED_RENDER_ENGINES = {'CYCLES', 'BLENDER_RENDER'} -TILE_SIZES = ( - ('16', "16", "16 x 16"), - ('32', "32", "32 x 32"), - ('64', "64", "64 x 64"), - ('128', "128", "128 x 128"), - ('256', "256", "256 x 256"), - ('512', "512", "512 x 512"), - ('1024', "1024", "1024 x 1024"), -) - - -def _update_tile_size(self, context): - do_set_tile_size(context) - - -class AutoTileSizeSettings(PropertyGroup): - gpu_choice: EnumProperty( - name="Target GPU Tile Size", - items=TILE_SIZES, - default='256', - description="Square dimensions of tiles for GPU rendering", - update=_update_tile_size - ) - cpu_choice: EnumProperty( - name="Target CPU Tile Size", - items=TILE_SIZES, - default='32', - description="Square dimensions of tiles for CPU rendering", - update=_update_tile_size - ) - bi_choice: EnumProperty( - name="Target CPU Tile Size", - items=TILE_SIZES, - default='64', - description="Square dimensions of tiles", - update=_update_tile_size - ) - gpu_custom: IntProperty( - name="Target Size", - default=256, - min=8, # same as blender's own limits - max=65536, - description="Custom target tile size for GPU rendering", - update=_update_tile_size - ) - cpu_custom: IntProperty( - name="Target Size", - default=32, - min=8, # same as blender's own limits - max=65536, - description="Custom target tile size for CPU rendering", - update=_update_tile_size - ) - bi_custom: IntProperty( - name="Target Size", - default=64, - min=8, # same as blender's own limits - max=65536, - description="Custom target tile size", - update=_update_tile_size - ) - target_type: EnumProperty( - name="Target tile size", - items=( - ('po2', "Po2", "A choice between powers of 2 (16, 32, 64...)"), - ('custom', "Custom", "Choose any number as the tile size target")), - default='po2', - description="Method of choosing the target tile size", - update=_update_tile_size - ) - use_optimal: BoolProperty( - name="Optimal Tiles", - default=True, - description="Try to find a similar tile size for best performance, " - "instead of using exact selected one", - update=_update_tile_size - ) - is_enabled: BoolProperty( - name="Auto Tile Size", - default=True, - description="Calculate the best tile size based on factors of the " - "render size and the chosen target", - update=_update_tile_size - ) - use_advanced_ui: BoolProperty( - name="Advanced Settings", - default=False, - description="Show extra options for more control over the calculated tile size" - ) - thread_error_correct: BoolProperty( - name="Fix", - default=True, - description="Reduce the tile size so that all your available threads are used", - update=_update_tile_size - ) - - # Internally used props (not for GUI) - first_run: BoolProperty( - default=True, - options={'HIDDEN'} - ) - threads_error: BoolProperty( - options={'HIDDEN'} - ) - num_tiles: IntVectorProperty( - default=(0, 0), - size=2, - options={'HIDDEN'} - ) - prev_choice: StringProperty( - default='', - options={'HIDDEN'} - ) - prev_engine: StringProperty( - default='', - options={'HIDDEN'} - ) - prev_device: StringProperty( - default='', - options={'HIDDEN'} - ) - prev_res: IntVectorProperty( - default=(0, 0), - size=2, - options={'HIDDEN'} - ) - prev_border: BoolProperty( - default=False, - options={'HIDDEN'} - ) - prev_border_res: FloatVectorProperty( - default=(0, 0, 0, 0), - size=4, - options={'HIDDEN'} - ) - prev_actual_tile_size: IntVectorProperty( - default=(0, 0), - size=2, - options={'HIDDEN'} - ) - prev_threads: IntProperty( - default=0, - options={'HIDDEN'} - ) - - -def ats_poll(context): - scene = context.scene - if scene.render.engine not in SUPPORTED_RENDER_ENGINES or not scene.ats_settings.is_enabled: - return False - return True - - -def engine_is_gpu(engine, device, userpref): - if engine == 'CYCLES' and device == 'GPU': - return userpref.addons['cycles'].preferences.has_active_device() - return False - - -def get_tilesize_prop(engine, device, userpref): - target_type = "_choice" if bpy.context.scene.ats_settings.target_type == 'po2' else "_custom" - if engine_is_gpu(engine, device, userpref): - return ("gpu" + target_type) - elif engine == 'CYCLES': - return ("cpu" + target_type) - return ("bi" + target_type) - - -@persistent -def on_scene_update(scene): - context = bpy.context - - if not ats_poll(context): - return - - userpref = context.preferences - - settings = scene.ats_settings - render = scene.render - engine = render.engine - - # scene.cycles might not always exist (Cycles is an addon)... - device = scene.cycles.device if engine == 'CYCLES' else settings.prev_device - border = render.use_border - threads = get_threads(context, device) - - choice = getattr(settings, get_tilesize_prop(engine, device, userpref)) - - res = get_actual_res(render) - actual_ts = (render.tile_x, render.tile_y) - border_res = (render.border_min_x, render.border_min_y, render.border_max_x, render.border_max_y) - - # detect relevant changes in scene - do_change = (engine != settings.prev_engine or - device != settings.prev_device or - border != settings.prev_border or - threads != settings.prev_threads or - str(choice) != settings.prev_choice or - res != settings.prev_res[:] or - border_res != settings.prev_border_res[:] or - actual_ts != settings.prev_actual_tile_size[:]) - if do_change: - do_set_tile_size(context) - - -def get_actual_res(render): - rend_percent = render.resolution_percentage * 0.01 - # floor is implicitly done by int conversion... - return (int(render.resolution_x * rend_percent), int(render.resolution_y * rend_percent)) - - -def get_threads(context, device): - render = context.scene.render - engine = render.engine - userpref = context.preferences - - if engine_is_gpu(engine, device, userpref): - threads = userpref.addons['cycles'].preferences.get_num_gpu_devices() - else: - threads = render.threads - - return threads - - -def max_tile_size(threads, xres, yres): - ''' Give the largest tile size that will still use all threads ''' - - render_area = xres * yres - tile_area = render_area / threads - tile_length = sqrt(tile_area) - - # lists: num x tiles, num y tiles, squareness, total tiles - perfect_attempts = [] # attempts with correct number of tiles - attempts = [] # all attempts, even if incorrect number of tiles - - axes = [xres, yres] - funcs = [floor, ceil] - - for axis in axes: - sec_axis = yres if axis == xres else xres - for func in funcs: - primary = func(axis / tile_length) - if primary > 0: - secondary = threads / primary - ts_p = axis / primary - ts_s = sec_axis / secondary - squareness = max(ts_p, ts_s) - min(ts_p, ts_s) - attempt = [primary if axis == xres else secondary, primary if - axis != xres else secondary, squareness, primary * secondary] - if attempt not in attempts: - attempts.append(attempt) - # will only be an integer if there are the right number of tiles - if secondary.is_integer(): - perfect_attempts.append(attempt) - - if perfect_attempts: # prefer to use attempt that has exactly the right number of tiles - attempts = perfect_attempts - - attempt = sorted(attempts, key=lambda k: k[2])[0] # pick set with most square tiles - numtiles_x = round(attempt[0]) - numtiles_y = round(attempt[1]) - tile_x = ceil(xres / numtiles_x) - tile_y = ceil(yres / numtiles_y) - - return (tile_x, tile_y) - - -def do_set_tile_size(context): - if not ats_poll(context): - return False - - scene = context.scene - userpref = context.preferences - - settings = scene.ats_settings - render = scene.render - engine = render.engine - device = scene.cycles.device if engine == 'CYCLES' else settings.prev_device - border = render.use_border - - realxres, realyres = xres, yres = res = get_actual_res(scene.render) - - if border: - xres = round(xres * (render.border_max_x - render.border_min_x)) - yres = round(yres * (render.border_max_y - render.border_min_y)) - - choice = getattr(settings, get_tilesize_prop(engine, device, userpref)) - target = int(choice) - - numtiles_x = ceil(xres / target) - numtiles_y = ceil(yres / target) - settings.num_tiles = (numtiles_x, numtiles_y) - if settings.use_optimal: - tile_x = ceil(xres / numtiles_x) - tile_y = ceil(yres / numtiles_y) - else: - tile_x = target - tile_y = target - - # Print tile size (for debug purposes) - # print("Tile size: %dx%d (%dx%d tiles)" % (tile_x, tile_y, ceil(xres / tile_x), ceil(yres / tile_y))) - - # Detect if there are fewer tiles than available threads - threads = get_threads(context, device) - if ((numtiles_x * numtiles_y) < threads): - settings.threads_error = True - if settings.thread_error_correct: - tile_x, tile_y = max_tile_size(threads, xres, yres) - settings.num_tiles = (ceil(xres / tile_x), ceil(yres / tile_y)) - else: - settings.threads_error = False - - # Make sure tile sizes are within the internal limit - tile_x = max(8, tile_x) - tile_y = max(8, tile_y) - tile_x = min(65536, tile_x) - tile_y = min(65536, tile_y) - - render.tile_x = tile_x - render.tile_y = tile_y - - settings.prev_engine = engine - settings.prev_device = device - settings.prev_border = border - settings.prev_threads = threads - settings.prev_choice = str(choice) - settings.prev_res = res - settings.prev_border_res = (render.border_min_x, render.border_min_y, - render.border_max_x, render.border_max_y) - settings.prev_actual_tile_size = (tile_x, tile_y) - settings.first_run = False - - return True - - -class SetTileSize(Operator): - bl_idname = "render.autotilesize_set" - bl_label = "Set" - bl_description = "The first render may not obey the tile-size set here" - - @classmethod - def poll(clss, context): - return ats_poll(context) - - def execute(self, context): - if do_set_tile_size(context): - return {'FINISHED'} - return {'CANCELLED'} - - -# ##### INTERFACE ##### - -def ui_layout(engine, layout, context): - scene = context.scene - userpref = context.preferences - - settings = scene.ats_settings - render = scene.render - engine = render.engine - device = scene.cycles.device if engine == 'CYCLES' else settings.prev_device - - col = layout.column(align=True) - sub = col.column(align=True) - row = sub.row(align=True) - row.prop(settings, "is_enabled", toggle=True) - row.prop(settings, "use_advanced_ui", toggle=True, text="", icon='PREFERENCES') - - sub = col.column(align=False) - sub.enabled = settings.is_enabled - - if settings.use_advanced_ui: - row = sub.row(align=True) - row.label(text="Target tile size:") - row.separator() - row.prop(settings, "target_type", expand=True) - - row = sub.row(align=True) - row.prop(settings, get_tilesize_prop(engine, device, userpref), expand=True) - sub.prop(settings, "use_optimal", text="Calculate Optimal Size") - - sub.label(text="Number of tiles: %s x %s (Total: %s)" % - (settings.num_tiles[0], settings.num_tiles[1], - settings.num_tiles[0] * settings.num_tiles[1]) - ) - - if settings.first_run: - sub = layout.column(align=True) - sub.operator("render.autotilesize_set", text="First-render fix", icon='ERROR') - elif settings.prev_device != device: - sub = layout.column(align=True) - sub.operator("render.autotilesize_set", text="Device changed - fix", icon='ERROR') - - # if not very square tile - if (render.tile_x / render.tile_y > 2) or (render.tile_x / render.tile_y < 0.5): - sub.label(text="Warning: Tile size is not very square", icon='ERROR') - sub.label(text=" Try a slightly different resolution") - - if settings.threads_error: - row = sub.row(align=True) - row.alignment = 'CENTER' - row.label(text="Warning: Fewer tiles than threads", icon='ERROR') - row.prop(settings, 'thread_error_correct') - - -def menu_func_cycles(self, context): - ui_layout('CYCLES', self.layout, context) - - -# ##### REGISTRATION ##### - -classes = ( - AutoTileSizeSettings, - SetTileSize -) - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Scene.ats_settings = PointerProperty( - type=AutoTileSizeSettings - ) - - # Note, the Cycles addon must be registered first, otherwise - # this panel doesn't exist - better be safe here! - cycles_panel = getattr(bpy.types, "CYCLES_RENDER_PT_performance", None) - if cycles_panel is not None: - cycles_panel.append(menu_func_cycles) - - bpy.app.handlers.depsgraph_update_post.append(on_scene_update) - - -def unregister(): - bpy.app.handlers.depsgraph_update_post.remove(on_scene_update) - - cycles_panel = getattr(bpy.types, "CYCLES_RENDER_PT_performance", None) - if cycles_panel is not None: - cycles_panel.remove(menu_func_cycles) - - del bpy.types.Scene.ats_settings - - for cls in reversed(classes): - bpy.utils.unregister_class(cls) - - -if __name__ == "__main__": - register() |