Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Kim <pk15950@gmail.com>2021-10-03 06:16:58 +0300
committerPeter Kim <pk15950@gmail.com>2021-10-03 06:16:58 +0300
commitc64726810ba781d980921947ba819b1364689e53 (patch)
treeebab21045e0ecef92475c55a6fb072435661a3fd
parenta85360cbdfbbee2bb46bcb93900f597a989bd33b (diff)
parent13140e3947af5c1becd752e7fb2c627b3ed95d96 (diff)
Merge branch 'master' into xr-controller-supportxr-controller-support
-rw-r--r--archipack/__init__.py4
-rw-r--r--archipack/archipack_autoboolean.py6
-rw-r--r--archipack/archipack_material.py2
-rw-r--r--archipack/archipack_preset.py4
-rw-r--r--blenderkit/overrides.py5
-rw-r--r--blenderkit/resolutions.py25
-rw-r--r--blenderkit/search.py9
-rw-r--r--blenderkit/ui_panels.py48
-rw-r--r--blenderkit/version_checker.py11
-rw-r--r--io_scene_fbx/__init__.py2
-rw-r--r--io_scene_fbx/export_fbx_bin.py5
-rw-r--r--io_scene_fbx/import_fbx.py5
-rwxr-xr-xio_scene_gltf2/__init__.py2
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py10
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py2
-rw-r--r--pose_library/gui.py53
-rw-r--r--render_auto_tile_size.py500
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()