From cfe62a042d5ebd3cca2b44fbf7cf345afa336951 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Tue, 30 Jul 2024 18:21:40 +0200 Subject: [PATCH 01/31] feat(blender): Upgrade to 3.6.14 --- blenderproc/python/utility/InstallUtility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index 74e5ec923..4d4198cfe 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -79,8 +79,8 @@ def make_sure_blender_is_installed(custom_blender_path: str, blender_install_pat # Determine configured version # right new only support blender-3.5.1 - major_version = "3.5" - minor_version = "1" + major_version = "3.6" + minor_version = "14" blender_version = f"blender-{major_version}.{minor_version}" if platform in ["linux", "linux2"]: blender_version += "-linux-x64" From 22a85de8f8f9b98cbed71ff5b6e66b12c2239786 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Tue, 30 Jul 2024 18:27:17 +0200 Subject: [PATCH 02/31] chore(utility): Corrected typing --- blenderproc/python/utility/InstallUtility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index 4d4198cfe..19c41e81d 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -9,7 +9,7 @@ from sys import platform, version_info import ssl from platform import machine -from typing import Union, Tuple +from typing import Union, Tuple, Optional if version_info.major == 3: from urllib.request import urlretrieve, build_opener, install_opener @@ -47,7 +47,7 @@ def determine_blender_install_path(used_args: "argparse.NameSpace") -> Union[str return custom_blender_path, blender_install_path @staticmethod - def make_sure_blender_is_installed(custom_blender_path: str, blender_install_path: str, + def make_sure_blender_is_installed(custom_blender_path: Optional[str], blender_install_path: str, reinstall_blender: bool = False) -> Tuple[str, str]: """ Make sure blender is installed. From c7c54e3dc603586db41f139f6d914b9c2c2ac24e Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 1 Aug 2024 11:30:42 +0200 Subject: [PATCH 03/31] feat(blender): upgrade to 4.0.2 --- blenderproc/python/utility/InstallUtility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index 19c41e81d..1908b1a54 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -79,8 +79,8 @@ def make_sure_blender_is_installed(custom_blender_path: Optional[str], blender_i # Determine configured version # right new only support blender-3.5.1 - major_version = "3.6" - minor_version = "14" + major_version = "4.0" + minor_version = "2" blender_version = f"blender-{major_version}.{minor_version}" if platform in ["linux", "linux2"]: blender_version += "-linux-x64" From e72a78094ed1274e952f8469848ad012916c35b2 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 1 Aug 2024 11:41:29 +0200 Subject: [PATCH 04/31] feat(blender): adjusted PrincipledBSDF input names to 4.0 --- blenderproc/python/loader/AMASSLoader.py | 7 +++---- blenderproc/python/loader/CCMaterialLoader.py | 2 +- blenderproc/python/loader/Front3DLoader.py | 4 ++-- blenderproc/python/material/Dust.py | 2 +- blenderproc/python/material/MaterialLoaderUtility.py | 4 ++-- blenderproc/python/utility/InstallUtility.py | 2 +- examples/advanced/auto_shading/main.py | 2 +- examples/advanced/random_backgrounds/main.py | 2 +- examples/advanced/urdf_loading_and_manipulation/main.py | 6 +++--- examples/datasets/bop_challenge/main_hb_random.py | 2 +- examples/datasets/bop_challenge/main_icbin_random.py | 2 +- examples/datasets/bop_challenge/main_itodd_random.py | 2 +- examples/datasets/bop_challenge/main_lm_upright.py | 2 +- examples/datasets/bop_challenge/main_tless_random.py | 2 +- examples/datasets/bop_challenge/main_tudl_random.py | 2 +- examples/datasets/bop_challenge/main_ycbv_random.py | 2 +- .../datasets/bop_object_on_surface_sampling/README.md | 2 +- examples/datasets/bop_object_on_surface_sampling/main.py | 2 +- .../datasets/bop_object_physics_positioning/README.md | 2 +- examples/datasets/bop_object_physics_positioning/main.py | 2 +- examples/datasets/suncg_with_improved_mat/README.md | 8 ++++---- examples/datasets/suncg_with_improved_mat/main.py | 6 +++--- 22 files changed, 33 insertions(+), 34 deletions(-) diff --git a/blenderproc/python/loader/AMASSLoader.py b/blenderproc/python/loader/AMASSLoader.py index b428b3d21..c7c2cbbe5 100644 --- a/blenderproc/python/loader/AMASSLoader.py +++ b/blenderproc/python/loader/AMASSLoader.py @@ -285,13 +285,12 @@ def correct_materials(objects: List[MeshObject]): skin_tone_fac = random.uniform(0.0, 1) skin_tone_rgb = [value * skin_tone_fac for value in skin_tone_rgb] principled_bsdf.inputs["Base Color"].default_value = mathutils.Vector([*skin_tone_rgb, 1.0]) - principled_bsdf.inputs["Subsurface"].default_value = 0.2 - principled_bsdf.inputs["Subsurface Color"].default_value = mathutils.Vector([*skin_tone_rgb, 1.0]) + principled_bsdf.inputs["Subsurface Weight"].default_value = 0.2 principled_bsdf.inputs["Subsurface Radius"].default_value = mathutils.Vector([1.0, 0.2, 0.1]) - principled_bsdf.inputs["Subsurface IOR"].default_value = 2.5 + principled_bsdf.inputs["IOR"].default_value = 2.5 # darker skin looks better when made less specular - principled_bsdf.inputs["Specular"].default_value = np.mean(skin_tone_rgb) / 255.0 + principled_bsdf.inputs["Specular IOR Level"].default_value = np.mean(skin_tone_rgb) / 255.0 texture_nodes = material.get_nodes_with_type("ShaderNodeTexImage") if texture_nodes and len(texture_nodes) > 1: diff --git a/blenderproc/python/loader/CCMaterialLoader.py b/blenderproc/python/loader/CCMaterialLoader.py index b4e7e455d..d06cdef36 100644 --- a/blenderproc/python/loader/CCMaterialLoader.py +++ b/blenderproc/python/loader/CCMaterialLoader.py @@ -151,7 +151,7 @@ def create_material(new_mat: bpy.types.Material, base_image_path: str, ambient_o base_color = MaterialLoaderUtility.add_base_color(nodes, links, base_image_path, principled_bsdf) collection_of_texture_nodes.append(base_color) - principled_bsdf.inputs["Specular"].default_value = 0.333 + principled_bsdf.inputs["Specular IOR Level"].default_value = 0.333 ao_node = MaterialLoaderUtility.add_ambient_occlusion(nodes, links, ambient_occlusion_image_path, principled_bsdf, base_color) diff --git a/blenderproc/python/loader/Front3DLoader.py b/blenderproc/python/loader/Front3DLoader.py index e2f484595..c6a3d455b 100644 --- a/blenderproc/python/loader/Front3DLoader.py +++ b/blenderproc/python/loader/Front3DLoader.py @@ -368,11 +368,11 @@ def load_furniture_objs(data: dict, future_model_path: str, lamp_light_strength: # Front3d .mtl files contain emission color which make the object mistakenly emissive # => Reset the emission color - principled_node.inputs["Emission"].default_value[:3] = [0, 0, 0] + principled_node.inputs["Emission Color"].default_value[:3] = [0, 0, 0] # Front3d .mtl files use Tf incorrectly, they make all materials fully transmissive # Revert that: - principled_node.inputs["Transmission"].default_value = 0 + principled_node.inputs["Transmission Weight"].default_value = 0 # For each a texture node image_node = mat.new_node('ShaderNodeTexImage') diff --git a/blenderproc/python/material/Dust.py b/blenderproc/python/material/Dust.py index 905907a2a..d0efd2c50 100644 --- a/blenderproc/python/material/Dust.py +++ b/blenderproc/python/material/Dust.py @@ -146,7 +146,7 @@ def add_dust(material: Material, strength: float, texture_nodes: List[bpy.types. # the used dust color is a grey with a tint in orange dust_color.inputs["Base Color"].default_value = [0.8, 0.773, 0.7, 1.0] dust_color.inputs["Roughness"].default_value = 1.0 - dust_color.inputs["Specular"].default_value = 0.0 + dust_color.inputs["Specular IOR Level"].default_value = 0.0 links.new(dust_color.outputs["BSDF"], mix_shader.inputs[2]) # create the input and output nodes inside of the group diff --git a/blenderproc/python/material/MaterialLoaderUtility.py b/blenderproc/python/material/MaterialLoaderUtility.py index e00835d03..53fb1d9d1 100644 --- a/blenderproc/python/material/MaterialLoaderUtility.py +++ b/blenderproc/python/material/MaterialLoaderUtility.py @@ -227,7 +227,7 @@ def add_specular(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks, specular_im if os.path.exists(specular_image_path): specular_texture = create_image_node(nodes, specular_image_path, True, _x_texture_node, 0) - links.new(specular_texture.outputs["Color"], principled_bsdf.inputs["Specular"]) + links.new(specular_texture.outputs["Color"], principled_bsdf.inputs["Specular IOR Level"]) return specular_texture return None @@ -482,7 +482,7 @@ def change_to_texture_less_render(use_alpha_channel): principled_bsdf = Utility.get_the_one_node_with_type(nodes, "BsdfPrincipled") # setting the color values for the shader - principled_bsdf.inputs['Specular'].default_value = 0.65 # specular + principled_bsdf.inputs['Specular IOR Level'].default_value = 0.65 # specular principled_bsdf.inputs['Roughness'].default_value = 0.2 # roughness for used_object in [obj for obj in bpy.context.scene.objects if hasattr(obj.data, 'materials')]: diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index 1908b1a54..a13989bdd 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -78,7 +78,7 @@ def make_sure_blender_is_installed(custom_blender_path: Optional[str], blender_i blender_install_path = "blender" # Determine configured version - # right new only support blender-3.5.1 + # right now only support blender-4.0.2 major_version = "4.0" minor_version = "2" blender_version = f"blender-{major_version}.{minor_version}" diff --git a/examples/advanced/auto_shading/main.py b/examples/advanced/auto_shading/main.py index 7cac0d670..2f2fd4a63 100644 --- a/examples/advanced/auto_shading/main.py +++ b/examples/advanced/auto_shading/main.py @@ -15,7 +15,7 @@ # Change specular and roughness factor of all objects, so the shading will be better visible for obj in objs: for mat in obj.get_materials(): - mat.set_principled_shader_value("Specular", 1) + mat.set_principled_shader_value("Specular IOR Level", 1) mat.set_principled_shader_value("Roughness", 0.3) # Find the object with name "Sphere" diff --git a/examples/advanced/random_backgrounds/main.py b/examples/advanced/random_backgrounds/main.py index 64f03a469..a23dc4de6 100644 --- a/examples/advanced/random_backgrounds/main.py +++ b/examples/advanced/random_backgrounds/main.py @@ -17,7 +17,7 @@ # Randomly perturbate the material of the object mat = obj.get_materials()[0] -mat.set_principled_shader_value("Specular", random.uniform(0, 1)) +mat.set_principled_shader_value("Specular IOR Level", random.uniform(0, 1)) mat.set_principled_shader_value("Roughness", random.uniform(0, 1)) mat.set_principled_shader_value("Base Color", np.random.uniform([0, 0, 0, 1], [1, 1, 1, 1])) mat.set_principled_shader_value("Metallic", random.uniform(0, 1)) diff --git a/examples/advanced/urdf_loading_and_manipulation/main.py b/examples/advanced/urdf_loading_and_manipulation/main.py index 59c4967f5..16fc0714d 100644 --- a/examples/advanced/urdf_loading_and_manipulation/main.py +++ b/examples/advanced/urdf_loading_and_manipulation/main.py @@ -21,17 +21,17 @@ if 'mica' in link.get_name() and not 'drive' in link.get_name(): if 'distal' in link.get_name(): mat.set_principled_shader_value("Roughness", 0.3) - mat.set_principled_shader_value("Specular", 0.8) + mat.set_principled_shader_value("Specular IOR Level", 0.8) mat.set_principled_shader_value("Base Color", [0.447, 0.3607, 0.3902, 1]) link.visuals[0].set_shading_mode('FLAT') else: mat.set_principled_shader_value("Roughness", 0.1) mat.set_principled_shader_value("Metallic", 1.0) - mat.set_principled_shader_value("Specular", 0.6) + mat.set_principled_shader_value("Specular IOR Level", 0.6) else: mat.set_principled_shader_value("Metallic", 0.3) mat.set_principled_shader_value("Roughness", 0.4) - mat.set_principled_shader_value("Specular", 0.9) + mat.set_principled_shader_value("Specular IOR Level", 0.9) # rotate every joint for 0.1 radians relative to the previous position robot.set_rotation_euler_fk(link=None, rotation_euler=0.2, mode='relative', frame=0) diff --git a/examples/datasets/bop_challenge/main_hb_random.py b/examples/datasets/bop_challenge/main_hb_random.py index 412beaf81..931091029 100644 --- a/examples/datasets/bop_challenge/main_hb_random.py +++ b/examples/datasets/bop_challenge/main_hb_random.py @@ -75,7 +75,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): grey_col = np.random.uniform(0.1, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99) obj.hide(False) diff --git a/examples/datasets/bop_challenge/main_icbin_random.py b/examples/datasets/bop_challenge/main_icbin_random.py index 8e8a3d2ac..e7759c824 100644 --- a/examples/datasets/bop_challenge/main_icbin_random.py +++ b/examples/datasets/bop_challenge/main_icbin_random.py @@ -73,7 +73,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): grey_col = np.random.uniform(0.1, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99) obj.hide(False) diff --git a/examples/datasets/bop_challenge/main_itodd_random.py b/examples/datasets/bop_challenge/main_itodd_random.py index 47ad1db2f..762880986 100644 --- a/examples/datasets/bop_challenge/main_itodd_random.py +++ b/examples/datasets/bop_challenge/main_itodd_random.py @@ -72,7 +72,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 0.5)) if obj.get_cp("bop_dataset_name") == 'itodd': - mat.set_principled_shader_value("Specular", np.random.uniform(0.3, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.3, 1.0)) mat.set_principled_shader_value("Metallic", np.random.uniform(0, 1.0)) if obj.get_cp("bop_dataset_name") == 'tless': mat.set_principled_shader_value("Metallic", np.random.uniform(0, 0.5)) diff --git a/examples/datasets/bop_challenge/main_lm_upright.py b/examples/datasets/bop_challenge/main_lm_upright.py index 73428bd2b..834c40392 100644 --- a/examples/datasets/bop_challenge/main_lm_upright.py +++ b/examples/datasets/bop_challenge/main_lm_upright.py @@ -73,7 +73,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): grey_col = np.random.uniform(0.1, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) obj.hide(False) # Sample two light sources diff --git a/examples/datasets/bop_challenge/main_tless_random.py b/examples/datasets/bop_challenge/main_tless_random.py index 456e9a355..c30c3d407 100644 --- a/examples/datasets/bop_challenge/main_tless_random.py +++ b/examples/datasets/bop_challenge/main_tless_random.py @@ -78,7 +78,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): if obj.get_cp("bop_dataset_name") == 'itodd': mat.set_principled_shader_value("Metallic", np.random.uniform(0.5, 1.0)) if obj.get_cp("bop_dataset_name") == 'tless': - mat.set_principled_shader_value("Specular", np.random.uniform(0.3, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.3, 1.0)) mat.set_principled_shader_value("Metallic", np.random.uniform(0, 0.5)) obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99) obj.hide(False) diff --git a/examples/datasets/bop_challenge/main_tudl_random.py b/examples/datasets/bop_challenge/main_tudl_random.py index 904858eb5..4ffdf6524 100644 --- a/examples/datasets/bop_challenge/main_tudl_random.py +++ b/examples/datasets/bop_challenge/main_tudl_random.py @@ -75,7 +75,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): grey_col = np.random.uniform(0.1, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99) obj.hide(False) diff --git a/examples/datasets/bop_challenge/main_ycbv_random.py b/examples/datasets/bop_challenge/main_ycbv_random.py index 6f7fea563..22ea36cca 100644 --- a/examples/datasets/bop_challenge/main_ycbv_random.py +++ b/examples/datasets/bop_challenge/main_ycbv_random.py @@ -75,7 +75,7 @@ def sample_pose_func(obj: bproc.types.MeshObject): grey_col = np.random.uniform(0.1, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99) obj.hide(False) diff --git a/examples/datasets/bop_object_on_surface_sampling/README.md b/examples/datasets/bop_object_on_surface_sampling/README.md index 1076ba3ea..14d223918 100644 --- a/examples/datasets/bop_object_on_surface_sampling/README.md +++ b/examples/datasets/bop_object_on_surface_sampling/README.md @@ -93,7 +93,7 @@ for j, obj in enumerate(sampled_bop_objs + distractor_bop_objs): grey_col = np.random.uniform(0.3, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) ``` * Sample grey colors for T-LESS object's materials using `np.random.uniform(0.3, 0.9)` function. diff --git a/examples/datasets/bop_object_on_surface_sampling/main.py b/examples/datasets/bop_object_on_surface_sampling/main.py index 78fe80d1c..146546cae 100644 --- a/examples/datasets/bop_object_on_surface_sampling/main.py +++ b/examples/datasets/bop_object_on_surface_sampling/main.py @@ -41,7 +41,7 @@ grey_col = np.random.uniform(0.3, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) # create room room_planes = [bproc.object.create_primitive('PLANE', scale=[2, 2, 1]), diff --git a/examples/datasets/bop_object_physics_positioning/README.md b/examples/datasets/bop_object_physics_positioning/README.md index 41d6dd241..c2524962d 100644 --- a/examples/datasets/bop_object_physics_positioning/README.md +++ b/examples/datasets/bop_object_physics_positioning/README.md @@ -97,7 +97,7 @@ for j, obj in enumerate(sampled_bop_objs + distractor_bop_objs): grey_col = np.random.uniform(0.3, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) ``` * Sample grey colors for T-LESS object's materials using `mat.set_principled_shader_value()` function. diff --git a/examples/datasets/bop_object_physics_positioning/main.py b/examples/datasets/bop_object_physics_positioning/main.py index 1905f5afa..da3e7007d 100644 --- a/examples/datasets/bop_object_physics_positioning/main.py +++ b/examples/datasets/bop_object_physics_positioning/main.py @@ -43,7 +43,7 @@ grey_col = np.random.uniform(0.1, 0.9) mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1]) mat.set_principled_shader_value("Roughness", np.random.uniform(0, 1.0)) - mat.set_principled_shader_value("Specular", np.random.uniform(0, 1.0)) + mat.set_principled_shader_value("Specular IOR Level", np.random.uniform(0, 1.0)) # create room room_planes = [bproc.object.create_primitive('PLANE', scale=[2, 2, 1]), diff --git a/examples/datasets/suncg_with_improved_mat/README.md b/examples/datasets/suncg_with_improved_mat/README.md index b04c45f7f..942f826a4 100644 --- a/examples/datasets/suncg_with_improved_mat/README.md +++ b/examples/datasets/suncg_with_improved_mat/README.md @@ -56,7 +56,7 @@ all_wood_materials = bproc.filter.by_attr(all_materials, "name", "wood.*|laminat # now change the used values for material in all_wood_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.05, 0.5)) - material.set_principled_shader_value("Specular", np.random.uniform(0.5, 1.0)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.5, 1.0)) material.set_displacement_from_principled_shader_value("Base Color", np.random.uniform(0.001, 0.15)) all_stone_materials = bproc.filter.by_attr(all_materials, "name", "tile.*|brick.*|stone.*", regex=True) @@ -64,14 +64,14 @@ all_stone_materials = bproc.filter.by_attr(all_materials, "name", "tile.*|brick. # now change the used values for material in all_stone_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.0, 0.2)) - material.set_principled_shader_value("Specular", np.random.uniform(0.9, 1.0)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.9, 1.0)) all_floor_materials = bproc.filter.by_attr(all_materials, "name", "carpet.*|textile.*", regex=True) # now change the used values for material in all_floor_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.5, 1.0)) - material.set_principled_shader_value("Specular", np.random.uniform(0.1, 0.3)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.1, 0.3)) ``` @@ -88,7 +88,7 @@ If you want to find out how your materials are named, click on the objects durin ```python for material in all_wood_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.05, 0.5)) - material.set_principled_shader_value("Specular", np.random.uniform(0.5, 1.0)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.5, 1.0)) material.set_displacement_from_principled_shader_value("Base Color", np.random.uniform(0.001, 0.15)) ``` diff --git a/examples/datasets/suncg_with_improved_mat/main.py b/examples/datasets/suncg_with_improved_mat/main.py index 7bfc95d6a..fba662f9a 100644 --- a/examples/datasets/suncg_with_improved_mat/main.py +++ b/examples/datasets/suncg_with_improved_mat/main.py @@ -45,7 +45,7 @@ # now change the used values for material in all_wood_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.05, 0.5)) - material.set_principled_shader_value("Specular", np.random.uniform(0.5, 1.0)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.5, 1.0)) material.set_displacement_from_principled_shader_value("Base Color", np.random.uniform(0.001, 0.15)) all_stone_materials = bproc.filter.by_attr(all_materials, "name", "tile.*|brick.*|stone.*", regex=True) @@ -53,14 +53,14 @@ # now change the used values for material in all_stone_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.0, 0.2)) - material.set_principled_shader_value("Specular", np.random.uniform(0.9, 1.0)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.9, 1.0)) all_floor_materials = bproc.filter.by_attr(all_materials, "name", "carpet.*|textile.*", regex=True) # now change the used values for material in all_floor_materials: material.set_principled_shader_value("Roughness", np.random.uniform(0.5, 1.0)) - material.set_principled_shader_value("Specular", np.random.uniform(0.1, 0.3)) + material.set_principled_shader_value("Specular IOR Level", np.random.uniform(0.1, 0.3)) # set the light bounces bproc.renderer.set_light_bounces(diffuse_bounces=200, glossy_bounces=200, max_bounces=200, transmission_bounces=200, transparent_max_bounces=200) From 847321eddf70f1a70c907c2122ee3ee2996dfe59 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Fri, 2 Aug 2024 12:29:35 +0200 Subject: [PATCH 05/31] feat(blender): use context.temp_override for operator overrides New API in Blender 4.0, the first 'context' argument no longer exists. --- .../python/object/PhysicsSimulation.py | 9 ++-- blenderproc/python/types/EntityUtility.py | 9 ++-- blenderproc/python/types/MeshObjectUtility.py | 53 ++++++++++--------- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/blenderproc/python/object/PhysicsSimulation.py b/blenderproc/python/object/PhysicsSimulation.py index bdd4fad8b..516b34189 100644 --- a/blenderproc/python/object/PhysicsSimulation.py +++ b/blenderproc/python/object/PhysicsSimulation.py @@ -47,7 +47,8 @@ def simulate_physics_and_fix_final_poses(min_simulation_time: float = 4.0, max_s obj_poses_after_sim = _PhysicsSimulation.get_pose() # Make sure to remove the simulation cache as we are only interested in the final poses - bpy.ops.ptcache.free_bake({"point_cache": bpy.context.scene.rigidbody_world.point_cache}) + with bpy.context.temp_override(point_cache=bpy.context.scene.rigidbody_world.point_cache): + bpy.ops.ptcache.free_bake() # Fix the pose of all objects to their pose at the end of the simulation (also revert origin shift) for obj in get_all_mesh_objects(): @@ -184,7 +185,8 @@ def do_simulation(min_simulation_time: float, max_simulation_time: float, check_ # Simulate current interval point_cache.frame_end = current_frame with stdout_redirected(enabled=not verbose): - bpy.ops.ptcache.bake({"point_cache": point_cache}, bake=True) + with bpy.context.temp_override(point_cache=point_cache): + bpy.ops.ptcache.bake(bake=True) # Go to second last frame and get poses bpy.context.scene.frame_set(current_frame - _PhysicsSimulation.seconds_to_frames(1)) @@ -205,7 +207,8 @@ def do_simulation(min_simulation_time: float, max_simulation_time: float, check_ else: # Free bake (this will not completely remove the simulation cache, so further simulations can # reuse the already calculated frames) - bpy.ops.ptcache.free_bake({"point_cache": point_cache}) + with bpy.context.temp_override(point_cache=point_cache): + bpy.ops.ptcache.free_bake() @staticmethod def get_pose() -> dict: diff --git a/blenderproc/python/types/EntityUtility.py b/blenderproc/python/types/EntityUtility.py index 5e4c71d9f..d4ca74063 100644 --- a/blenderproc/python/types/EntityUtility.py +++ b/blenderproc/python/types/EntityUtility.py @@ -236,7 +236,8 @@ def delete(self, remove_all_offspring: bool = False): selected_objects = [self] if remove_all_offspring: selected_objects.extend(self.get_children(return_all_offspring=True)) - bpy.ops.object.delete({"selected_objects": [e.blender_obj for e in selected_objects]}) + with bpy.context.temp_override(selected_objects=[e.blender_obj for e in selected_objects]): + bpy.ops.object.delete() def is_empty(self) -> bool: """ Returns whether the entity is from type "EMPTY". @@ -338,6 +339,8 @@ def delete_multiple(entities: List[Union["Entity"]], remove_all_offspring: bool all_nodes.extend(entity.get_children(return_all_offspring=True)) # avoid doubles all_nodes = set(all_nodes) - bpy.ops.object.delete({"selected_objects": [e.blender_obj for e in all_nodes]}) + with bpy.context.temp_override(selected_objects=[e.blender_obj for e in all_nodes]): + bpy.ops.object.delete() else: - bpy.ops.object.delete({"selected_objects": [e.blender_obj for e in entities]}) + with bpy.context.temp_override(selected_objects=[e.blender_obj for e in entities]): + bpy.ops.object.delete() diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 136484d35..1885102e7 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -145,8 +145,8 @@ def persist_transformation_into_mesh(self, location: bool = True, rotation: bool :param rotation: Determines whether the object's rotation should be persisted. :param scale: Determines whether the object's scale should be persisted. """ - bpy.ops.object.transform_apply({"selected_editable_objects": [self.blender_obj]}, location=location, - rotation=rotation, scale=scale) + with bpy.context.temp_override(selected_editable_objects=[self.blender_obj]): + bpy.ops.object.transform_apply(location=location, rotation=rotation, scale=scale) def get_origin(self) -> np.ndarray: """ Returns the origin of the object. @@ -167,21 +167,20 @@ def set_origin(self, point: Union[list, np.ndarray, Vector] = None, mode: str = "CENTER_OF_MASS", "CENTER_OF_VOLUME"] :return: The new origin in world coordinates. """ - context = {"selected_editable_objects": [self.blender_obj]} - - if mode == "POINT": - if point is None: - raise Exception("The parameter point is not given even though the mode is set to POINT.") - prev_cursor_location = bpy.context.scene.cursor.location.copy() - bpy.context.scene.cursor.location = point - bpy.ops.object.origin_set(context, type='ORIGIN_CURSOR') - bpy.context.scene.cursor.location = prev_cursor_location.copy() - elif mode == "CENTER_OF_MASS": - bpy.ops.object.origin_set(context, type='ORIGIN_CENTER_OF_MASS') - elif mode == "CENTER_OF_VOLUME": - bpy.ops.object.origin_set(context, type='ORIGIN_CENTER_OF_VOLUME') - else: - raise Exception("No such mode: " + mode) + with bpy.context.temp_override(selected_editable_objects=[self.blender_obj]): + if mode == "POINT": + if point is None: + raise Exception("The parameter point is not given even though the mode is set to POINT.") + prev_cursor_location = bpy.context.scene.cursor.location.copy() + bpy.context.scene.cursor.location = point + bpy.ops.object.origin_set(type='ORIGIN_CURSOR') + bpy.context.scene.cursor.location = prev_cursor_location.copy() + elif mode == "CENTER_OF_MASS": + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS') + elif mode == "CENTER_OF_VOLUME": + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME') + else: + raise Exception("No such mode: " + mode) return self.get_origin() @@ -208,7 +207,8 @@ def enable_rigidbody(self, active: bool, collision_shape: str = 'CONVEX_HULL', c :param linear_damping: Amount of linear velocity that is lost over time. """ # Enable rigid body component - bpy.ops.rigidbody.object_add({'object': self.blender_obj}) + with bpy.context.temp_override(object=self.blender_obj): + bpy.ops.rigidbody.object_add() # Sett attributes rigid_body = self.blender_obj.rigid_body rigid_body.type = "ACTIVE" if active else "PASSIVE" @@ -254,7 +254,8 @@ def build_convex_decomposition_collision_shape(self, vhacd_path: str, temp_dir: def disable_rigidbody(self): """ Disables the rigidbody element of the object """ if self.has_rigidbody_enabled(): - bpy.ops.rigidbody.object_remove({'object': self.blender_obj}) + with bpy.context.temp_override(object=self.blender_obj): + bpy.ops.rigidbody.object_remove() else: warnings.warn(f"MeshObject {self.get_name()} has no rigid_body component enabled") @@ -340,13 +341,14 @@ def join_with_other_objects(self, objects: List["MeshObject"]): :param objects: List of objects which will be merged with this object """ context = {} - # save selection context["object"] = context["active_object"] = self.blender_obj + # save selection # select all objects which will be merged with the target context["selected_objects"] = context["selected_editable_objects"] = [obj.blender_obj for obj in objects] + \ [self.blender_obj] - # execute the joining operation - bpy.ops.object.join(context) + with bpy.context.temp_override(**context): + # execute the joining operation + bpy.ops.object.join() def edit_mode(self): """ Switch into edit mode of this mesh object """ @@ -492,7 +494,9 @@ def add_modifier(self, name: str, **kwargs): :param kwargs: Additional attributes that should be set to the modifier. """ # Create the new modifier - bpy.ops.object.modifier_add({"object": self.blender_obj}, type=name) + with bpy.context.temp_override(object=self.blender_obj): + bpy.ops.object.modifier_add(type=name) + # Set the attributes modifier = self.blender_obj.modifiers[-1] for key, value in kwargs.items(): @@ -502,7 +506,8 @@ def add_geometry_nodes(self): """ Adds a new geometry nodes modifier to the object. """ # Create the new modifier - bpy.ops.node.new_geometry_nodes_modifier({"object": self.blender_obj}) + with bpy.context.temp_override(object=self.blender_obj): + bpy.ops.node.new_geometry_nodes_modifier() modifier = self.blender_obj.modifiers[-1] return modifier.node_group From ecca5cf4611cdc45aa549ba1543c814cf2368d70 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Fri, 2 Aug 2024 16:50:15 +0200 Subject: [PATCH 06/31] feat(blender): use the node_tree.interface API instead of .outputs and .inputs The latter was deprecated in favor of the prior. --- blenderproc/python/material/Dust.py | 15 +++++++++++---- blenderproc/python/utility/BlenderUtility.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/blenderproc/python/material/Dust.py b/blenderproc/python/material/Dust.py index d0efd2c50..564408e84 100644 --- a/blenderproc/python/material/Dust.py +++ b/blenderproc/python/material/Dust.py @@ -156,10 +156,17 @@ def add_dust(material: Material, strength: float, texture_nodes: List[bpy.types. group_input.location = (x_pos + x_diff * 7, y_pos - y_diff * 0.5) # create sockets for the outside of the group match them to the mix shader - group.outputs.new(mix_shader.outputs[0].bl_idname, mix_shader.outputs[0].name) - group.inputs.new(mix_shader.inputs[1].bl_idname, mix_shader.inputs[1].name) - group.inputs.new(multiply_node.inputs[1].bl_idname, "Dust strength") - group.inputs.new(mapping_node.inputs["Scale"].bl_idname, "Texture scale") + group.interface.new_socket( + mix_shader.outputs[0].name, in_out='OUTPUT', socket_type=mix_shader.outputs[0].bl_idname) + group.interface.new_socket( + mix_shader.inputs[1].name, in_out='INPUT', socket_type=mix_shader.inputs[1].bl_idname) + group.interface.new_socket( + "Dust strength", in_out='INPUT', socket_type=multiply_node.inputs[1].bl_idname) + # We set the socket_type='NodeSocketVector' directly instead of using + # 'mapping.node.inputs["Scale"].bl_idname', because the 'Scale' has a specific bl_idname of + # 'NodeSocketVectorXYZ', but 'new_socket' expects 'NodeSocketVector'. + group.interface.new_socket( + "Texture scale", in_out='INPUT', socket_type='NodeSocketVector') # link the input and output to the mix shader links.new(group_input.outputs[0], mix_shader.inputs[1]) diff --git a/blenderproc/python/utility/BlenderUtility.py b/blenderproc/python/utility/BlenderUtility.py index f83c774f2..51483ee43 100644 --- a/blenderproc/python/utility/BlenderUtility.py +++ b/blenderproc/python/utility/BlenderUtility.py @@ -368,7 +368,7 @@ def add_nodes_to_group(nodes: bpy.types.Node, group_name: str) -> bpy.types.Shad material_outputs = Utility.get_nodes_with_type(group.nodes, "OutputMaterial") if len(material_outputs) == 1: for input_node in material_outputs[0].inputs: - group.outputs.new(input_node.bl_idname, input_node.name) + group.interface.new_socket(input_node.name, in_out='OUTPUT', socket_type=input_node.bl_idname) for link in input_node.links: group.links.new(link.from_socket, group_output.inputs[input_node.name]) # remove the material output, the material output should never be inside of a group From 2dbf9828d8fa21383db3b7df6744d826e17ad322 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Tue, 6 Aug 2024 14:59:32 +0200 Subject: [PATCH 07/31] feat(blender): remove the use_legacy_obj_import This is no longer available from Blender 4.1 --- blenderproc/python/loader/ObjectLoader.py | 10 ++-------- blenderproc/python/loader/ShapeNetLoader.py | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/blenderproc/python/loader/ObjectLoader.py b/blenderproc/python/loader/ObjectLoader.py index c24b8cb59..9b3f87182 100644 --- a/blenderproc/python/loader/ObjectLoader.py +++ b/blenderproc/python/loader/ObjectLoader.py @@ -12,8 +12,7 @@ from blenderproc.python.material.MaterialLoaderUtility import create as create_material -def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]]] = None, - use_legacy_obj_import: bool = False, **kwargs) -> List[MeshObject]: +def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]]] = None, **kwargs) -> List[MeshObject]: """ Import all objects for the given file and returns the loaded objects In .obj files a list of objects can be saved in. @@ -22,8 +21,6 @@ def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]] :param filepath: the filepath to the location where the data is stored :param cached_objects: a dict of filepath to objects, which have been loaded before, to avoid reloading (the dict is updated in this function) - :param use_legacy_obj_import: If this is true the old legacy obj importer in python is used. It is slower, but - it correctly imports the textures in the ShapeNet dataset. :param kwargs: all other params are handed directly to the bpy loading fct. check the corresponding documentation :return: The list of loaded mesh objects. """ @@ -44,10 +41,7 @@ def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]] previously_selected_objects = bpy.context.selected_objects if filepath.endswith(".obj"): # load an .obj file: - if use_legacy_obj_import: - bpy.ops.import_scene.obj(filepath=filepath, **kwargs) - else: - bpy.ops.wm.obj_import(filepath=filepath, **kwargs) + bpy.ops.wm.obj_import(filepath=filepath, **kwargs) elif filepath.endswith(".ply"): PLY_TEXTURE_FILE_COMMENT = "comment TextureFile " model_name = os.path.basename(filepath) diff --git a/blenderproc/python/loader/ShapeNetLoader.py b/blenderproc/python/loader/ShapeNetLoader.py index 4302e71b0..c9a52ed95 100644 --- a/blenderproc/python/loader/ShapeNetLoader.py +++ b/blenderproc/python/loader/ShapeNetLoader.py @@ -39,7 +39,7 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", taxonomy_file_path, data_path) selected_obj = random.choice(files_with_fitting_synset) # with the new version the textures are all wrong - loaded_objects = load_obj(selected_obj, use_legacy_obj_import=True) + loaded_objects = load_obj(selected_obj) # In shapenet every .obj file only contains one object, make sure that is the case if len(loaded_objects) != 1: From 42215713e4a8187758d1dabc8238026be82a988a Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Tue, 6 Aug 2024 15:06:13 +0200 Subject: [PATCH 08/31] feat(blender): import ply using bpy.op.wm.ply_import The 'import_mesh.ply' was replaced by '.wm.ply_import'. --- blenderproc/python/loader/ObjectLoader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/loader/ObjectLoader.py b/blenderproc/python/loader/ObjectLoader.py index 9b3f87182..12ab1bdcc 100644 --- a/blenderproc/python/loader/ObjectLoader.py +++ b/blenderproc/python/loader/ObjectLoader.py @@ -72,11 +72,11 @@ def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]] file.write(new_ply_file_content) # Load .ply mesh - bpy.ops.import_mesh.ply(filepath=tmp_ply_file, **kwargs) + bpy.ops.wm.ply_import(filepath=tmp_ply_file, **kwargs) else: # If no texture was given # load a .ply mesh - bpy.ops.import_mesh.ply(filepath=filepath, **kwargs) + bpy.ops.wm.ply_import(filepath=filepath, **kwargs) # Create default material material = create_material('ply_material') material.map_vertex_color() From 0430a92d6882eedd74bc4826da510382b1880fc9 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 8 Aug 2024 13:22:43 +0200 Subject: [PATCH 09/31] feat(blender): bump version to 4.1.1 --- blenderproc/python/utility/InstallUtility.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index a13989bdd..2210ff371 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -78,9 +78,9 @@ def make_sure_blender_is_installed(custom_blender_path: Optional[str], blender_i blender_install_path = "blender" # Determine configured version - # right now only support blender-4.0.2 - major_version = "4.0" - minor_version = "2" + # right now only support blender-4.1.1 + major_version = "4.1" + minor_version = "1" blender_version = f"blender-{major_version}.{minor_version}" if platform in ["linux", "linux2"]: blender_version += "-linux-x64" From 442c947dee34a8fa7ce64d36fced5c6b67c6b047 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 8 Aug 2024 13:42:39 +0200 Subject: [PATCH 10/31] feat(blender): make pip packages setup work with blender 4.1 updated python --- blenderproc/python/utility/SetupUtility.py | 2 +- docs/run.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/utility/SetupUtility.py b/blenderproc/python/utility/SetupUtility.py index de1253da0..1cb09960f 100644 --- a/blenderproc/python/utility/SetupUtility.py +++ b/blenderproc/python/utility/SetupUtility.py @@ -98,7 +98,7 @@ def determine_python_paths(blender_path: Optional[str], major_version: Optional[ major_version = os.path.basename(os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..", ".."))) # Based on the OS determined the three paths - current_python_version = "python3.10" + current_python_version = "python3.11" if platform in ["linux", "linux2"]: python_bin_folder = os.path.join(blender_path, major_version, "python", "bin") python_bin = os.path.join(python_bin_folder, current_python_version) diff --git a/docs/run.py b/docs/run.py index 3c12934dc..9de50a550 100644 --- a/docs/run.py +++ b/docs/run.py @@ -4,7 +4,7 @@ from shutil import which # Add path to custom packages inside the blender main directory -sys.path.append(os.path.join(os.path.dirname(sys.executable), "..", "..", "..", "custom-python-packages/lib/python3.10/site-packages/")) +sys.path.append(os.path.join(os.path.dirname(sys.executable), "..", "..", "..", "custom-python-packages/python3.11/site-packages/")) # Determine abs path to sphinx-build sphinx_build_bin_path = os.path.join(os.path.dirname(sys.executable), "..", "..", "..", "custom-python-packages", "bin", "sphinx-build") From bb85745d0e81bf0295af36e9978f05e25aad3f2d Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 8 Aug 2024 13:44:35 +0200 Subject: [PATCH 11/31] feat(blender): use auto smooth geometry nodes In 4.1 the node group replaces the prior implementation of auto smooth on object data. --- blenderproc/python/types/MeshObjectUtility.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 1885102e7..092fabef3 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -96,14 +96,11 @@ def set_shading_mode(self, mode: str, angle_value: float = 30): """ if mode.lower() == "flat": is_smooth = False - self.blender_obj.data.use_auto_smooth = False elif mode.lower() == "smooth": is_smooth = True - self.blender_obj.data.use_auto_smooth = False elif mode.lower() == "auto": is_smooth = True - self.blender_obj.data.use_auto_smooth = True - self.blender_obj.data.auto_smooth_angle = np.deg2rad(angle_value) + self.add_auto_smooth_modifier(angle=angle_value) else: raise RuntimeError(f"This shading mode is unknown: {mode}") @@ -511,6 +508,21 @@ def add_geometry_nodes(self): modifier = self.blender_obj.modifiers[-1] return modifier.node_group + def add_auto_smooth_modifier(self, angle: float = 30.0): + """ Adds the 'Smooth by Angle' geometry nodes modifier. + + This replaces the 'Auto Smooth' behavior available in Blender before 4.1. + """ + with bpy.context.temp_override(object=self.blender_obj): + bpy.ops.object.modifier_add_node_group( + asset_library_type='CUSTOM', + asset_library_identifier="assets", + relative_asset_identifier="geometry_nodes/smooth_by_angle.blend/NodeTree/Smooth by Angle" + ) + + modifier = self.blender_obj.modifiers[-1] + modifier["Input_1"] = float(angle) + def mesh_as_trimesh(self) -> Trimesh: """ Returns a trimesh.Trimesh instance of the MeshObject. From 71bfebba525e6beb2c4d3051d3b319d8254b494e Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 8 Aug 2024 17:02:06 +0200 Subject: [PATCH 12/31] feat(blender): make depth, distance and segmentation output rgb in blender 4.1 Since Blender 4.1 the .exr depth outputs are one channel instead of three channels. The imageio.v3.imread using 'opencv' can't open single channel .exr. Thus using nodes we create three channel image output from the depth again. This might be fixed in future versions of Blender, fix to respect the file foramt 'Color' property (that's actually set to RGB) was already merged into master: https://projects.blender.org/blender/blender/commit/40be124184bb571669b7346a36003c33c412cb20 --- .../python/renderer/RendererUtility.py | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/blenderproc/python/renderer/RendererUtility.py b/blenderproc/python/renderer/RendererUtility.py index 2540913f0..cbcaae8b8 100644 --- a/blenderproc/python/renderer/RendererUtility.py +++ b/blenderproc/python/renderer/RendererUtility.py @@ -230,7 +230,6 @@ def enable_distance_output(activate_antialiasing: bool, output_dir: Optional[str mapper_node.inputs['From Max'].default_value = 1.0 mapper_node.inputs['To Min'].default_value = 0 mapper_node.inputs['To Max'].default_value = antialiasing_distance_max - final_output = mapper_node.outputs['Value'] # Build output node output_file = tree.nodes.new("CompositorNodeOutputFile") @@ -238,8 +237,15 @@ def enable_distance_output(activate_antialiasing: bool, output_dir: Optional[str output_file.format.file_format = "OPEN_EXR" output_file.file_slots.values()[0].path = file_prefix + # Feed the output through 'Combine Color' node, to create 3 channel RGB grayscale image as a lot of + # EXR readers don't support single float channel EXR files and Blender writes depth as a single + # channel since version 4.1.1 by default. + combine_color = tree.nodes.new("CompositorNodeCombineColor") + combine_color.mode = "HSV" + links.new(mapper_node.outputs["Value"], combine_color.inputs[2]) + # Feed the Z-Buffer or Mist output of the render layer to the input of the file IO layer - links.new(final_output, output_file.inputs['Image']) + links.new(combine_color.outputs["Image"], output_file.inputs['Image']) Utility.add_output_entry({ "key": output_key, @@ -299,8 +305,15 @@ def enable_depth_output(activate_antialiasing: bool, output_dir: Optional[str] = output_file.format.file_format = "OPEN_EXR" output_file.file_slots.values()[0].path = file_prefix - # Feed the Z-Buffer output of the render layer to the input of the file IO layer - links.new(render_layer_node.outputs["Depth"], output_file.inputs['Image']) + # Feed the output through 'Combine Color' node, to create 3 channel RGB grayscale image as a lot of + # EXR readers don't support single float channel EXR files and Blender writes depth as a single + # channel since version 4.1.1 by default + combine_color = tree.nodes.new("CompositorNodeCombineColor") + combine_color.mode = "HSV" + links.new(render_layer_node.outputs["Depth"], combine_color.inputs[2]) + + # Feed the Z-Buffer RGB output from the Combine Color node to the input of the file IO layer + links.new(combine_color.outputs["Image"], output_file.inputs["Image"]) Utility.add_output_entry({ "key": output_key, @@ -482,7 +495,14 @@ def enable_segmentation_output(map_by: Union[str, List[str]] = "category_id", "semantic_segmentation_default_values": default_values }) - links.new(render_layer_node.outputs["IndexOB"], output_node.inputs["Image"]) + # Feed the output through 'Combine Color' node, to create 3 channel RGB grayscale image as a lot of + # EXR readers don't support single float channel EXR files and Blender writes depth as a single + # channel since version 4.1.1 by default + combine_color = tree.nodes.new("CompositorNodeCombineColor") + combine_color.mode = "HSV" + links.new(render_layer_node.outputs["IndexOB"], combine_color.inputs[2]) + + links.new(combine_color.outputs["Image"], output_node.inputs["Image"]) # set the threshold low to avoid noise in alpha materials bpy.context.scene.view_layers["ViewLayer"].pass_alpha_threshold = pass_alpha_threshold From a66112acddaddbeb27f9a65366302c0c530a5c56 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 8 Aug 2024 17:52:30 +0200 Subject: [PATCH 13/31] feat(blender): use correct import in external/vhacd --- blenderproc/external/vhacd/decompose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blenderproc/external/vhacd/decompose.py b/blenderproc/external/vhacd/decompose.py index 20a220dbf..4d95a848a 100644 --- a/blenderproc/external/vhacd/decompose.py +++ b/blenderproc/external/vhacd/decompose.py @@ -150,7 +150,7 @@ def convex_decomposition(obj: "MeshObject", temp_dir: str, vhacd_path: str, reso else: out_file_name = os.path.join(cache_dir, str(mesh_hash) + ".obj") - bpy.ops.import_scene.obj(filepath=out_file_name, axis_forward="Y", axis_up="Z") + bpy.ops.wm.obj_import(filepath=out_file_name, forward_axis="Y", up_axis="Z") imported = bpy.context.selected_objects # Name and transform the loaded parts From 5940449135a7a20efa7ea657a66a9d0894f3a567 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Wed, 14 Aug 2024 10:56:16 +0200 Subject: [PATCH 14/31] fix(camera): use np.float32 instead of deprecated np.float --- blenderproc/python/camera/LensDistortionUtility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blenderproc/python/camera/LensDistortionUtility.py b/blenderproc/python/camera/LensDistortionUtility.py index f8ce8ff29..be44bf250 100644 --- a/blenderproc/python/camera/LensDistortionUtility.py +++ b/blenderproc/python/camera/LensDistortionUtility.py @@ -246,7 +246,7 @@ def _internal_apply(input_image: np.ndarray) -> np.ndarray: amount_of_output_channels = input_image.shape[2] image_distorted = np.zeros((orig_res_y, orig_res_x, amount_of_output_channels)) used_dtpye = input_image.dtype - data = input_image.astype(np.float) + data = input_image.astype(np.float32) # Forward mapping in order to distort the undistorted image coordinates # and reshape the arrays into the image shape grid. # The reference frame for coords is as in DLR CalDe etc. (the upper-left pixel center is at [0,0]) From 4c3ac3e8fe3ded1b6bf8cfc34bce1a9e32568b97 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Wed, 14 Aug 2024 11:16:58 +0200 Subject: [PATCH 15/31] feat(render): add view transform as 'render' parameter Since Blender 4.1 'AgX' is the new default, but to yield semi-similar results we want to keep the default as 'Filmic'. --- blenderproc/python/renderer/RendererUtility.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blenderproc/python/renderer/RendererUtility.py b/blenderproc/python/renderer/RendererUtility.py index cbcaae8b8..381e707d8 100644 --- a/blenderproc/python/renderer/RendererUtility.py +++ b/blenderproc/python/renderer/RendererUtility.py @@ -654,6 +654,7 @@ def _render_progress_bar(pipe_out: int, pipe_in: int, stdout: IO, total_frames: def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_key: Optional[str] = "colors", load_keys: Optional[Set[str]] = None, return_data: bool = True, keys_with_alpha_channel: Optional[Set[str]] = None, + view_transform: str = "Filmic", verbose: bool = False) -> Dict[str, Union[np.ndarray, List[np.ndarray]]]: """ Render all frames. @@ -666,6 +667,7 @@ def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_k :param load_keys: Set of output keys to load when available :param return_data: Whether to load and return generated data. :param keys_with_alpha_channel: A set containing all keys whose alpha channels should be loaded. + :param view_transform: Determines the view transform to use for rendering. :param verbose: If True, more details about the rendering process are printed. :return: dict of lists of raw renderer output. Keys can be 'distance', 'colors', 'normals' """ @@ -675,6 +677,10 @@ def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_k load_keys = {'colors', 'distance', 'normals', 'diffuse', 'depth', 'segmap'} keys_with_alpha_channel = {'colors'} if bpy.context.scene.render.film_transparent else None + if view_transform not in {"AgX", "Standard", "Filmic", "Filmic Log", "False Color", "Raw"}: + raise ValueError(f"Unknown view transform {view_transform}") + + bpy.context.scene.view_settings.view_transform = view_transform if output_key is not None: Utility.add_output_entry({ "key": output_key, From 152100f2639c632aedf3f1a50f9621b942ef1c87 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Wed, 14 Aug 2024 16:25:04 +0200 Subject: [PATCH 16/31] feat(blender): bump blender to 4.2.0 Adjusted breaking API changes, and adding of the smooth by angle modifier - which is now actually less prone to error. --- blenderproc/python/types/LightUtility.py | 2 +- blenderproc/python/types/MeshObjectUtility.py | 7 +++---- blenderproc/python/utility/InstallUtility.py | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/blenderproc/python/types/LightUtility.py b/blenderproc/python/types/LightUtility.py index a3152c399..9f30b55d6 100644 --- a/blenderproc/python/types/LightUtility.py +++ b/blenderproc/python/types/LightUtility.py @@ -113,7 +113,7 @@ def setup_as_projector(self, pattern: np.ndarray, frame: Optional[int] = None): self.blender_obj.data.use_nodes = True self.blender_obj.data.shadow_soft_size = 0 self.blender_obj.data.spot_size = 3.14159 # 180deg in rad - self.blender_obj.data.cycles.cast_shadow = False + self.blender_obj.data.use_shadow = False nodes = self.blender_obj.data.node_tree.nodes links = self.blender_obj.data.node_tree.links diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 092fabef3..496f503d1 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -515,10 +515,9 @@ def add_auto_smooth_modifier(self, angle: float = 30.0): """ with bpy.context.temp_override(object=self.blender_obj): bpy.ops.object.modifier_add_node_group( - asset_library_type='CUSTOM', - asset_library_identifier="assets", - relative_asset_identifier="geometry_nodes/smooth_by_angle.blend/NodeTree/Smooth by Angle" - ) + asset_library_type='ESSENTIALS', + asset_library_identifier="", + relative_asset_identifier="geometry_nodes/smooth_by_angle.blend/NodeTree/Smooth by Angle") modifier = self.blender_obj.modifiers[-1] modifier["Input_1"] = float(angle) diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index 2210ff371..5f202f284 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -78,9 +78,9 @@ def make_sure_blender_is_installed(custom_blender_path: Optional[str], blender_i blender_install_path = "blender" # Determine configured version - # right now only support blender-4.1.1 - major_version = "4.1" - minor_version = "1" + # right now only support blender-4.2.0 + major_version = "4.2" + minor_version = "0" blender_version = f"blender-{major_version}.{minor_version}" if platform in ["linux", "linux2"]: blender_version += "-linux-x64" From 6817ed6be5f532b5d3e88e7ffef0134c4300af3e Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Mon, 19 Aug 2024 13:19:31 +0200 Subject: [PATCH 17/31] feat(blender): load auto smooth node group manually Using the 'bpy.ops.modifier_add_node_group' would be superior, but it doesn't work correctly in background mode. --- blenderproc/python/types/MeshObjectUtility.py | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 496f503d1..80a708545 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -2,6 +2,7 @@ from typing import List, Union, Tuple, Optional from sys import platform +from pathlib import Path import warnings import bpy @@ -513,13 +514,37 @@ def add_auto_smooth_modifier(self, angle: float = 30.0): This replaces the 'Auto Smooth' behavior available in Blender before 4.1. """ - with bpy.context.temp_override(object=self.blender_obj): - bpy.ops.object.modifier_add_node_group( - asset_library_type='ESSENTIALS', - asset_library_identifier="", - relative_asset_identifier="geometry_nodes/smooth_by_angle.blend/NodeTree/Smooth by Angle") + # The bpy.ops.object.modifier_add_node_group doesn't work in background mode :( + # So we load the node group and create the modifier ourselves. + # Known issue: https://projects.blender.org/blender/blender/issues/117399 - modifier = self.blender_obj.modifiers[-1] + # The datafiles are expected to be in the same folder as the 'blender_binary/version'. + path = Path(bpy.app.binary_path).parent / "4.2" / "datafiles" / "assets" / "geometry_nodes" / "smooth_by_angle.blend" + if not path.exists(): + raise RuntimeError(f"Could not find the path to the 'ESSENTIALS' asset folder expected at {path}") + + # Get the node group from the current file (reuse if it exists), otherwise load it from the + # precalculated path and append to the current .blend. + smooth_by_angle_node_group_name = "Smooth by Angle" + existing_node_group = bpy.data.node_groups.get(smooth_by_angle_node_group_name, None) + if existing_node_group is None: + with bpy.data.libraries.load(str(path), link=False) as (data_from, data_to): + data_to.node_groups = [smooth_by_angle_node_group_name] + existing_node_group = data_to.node_groups[0] + + # Check if the modifier already exists + modifier = None + for existing_mod in self.blender_obj.modifiers: + if existing_mod.type == 'NODES' and existing_mod.node_group == existing_node_group: + modifier = modifier + break + + # Create a new modifier if no existing modifier was found + if modifier is None: + modifier = self.blender_obj.modifiers.new(name=smooth_by_angle_node_group_name, type='NODES') + modifier.node_group = existing_node_group + + modifier = self.blender_obj.modifiers["Smooth by Angle"] modifier["Input_1"] = float(angle) def mesh_as_trimesh(self) -> Trimesh: From 4deeb74bc3bc105969d344824c6fff13099166d5 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Mon, 19 Aug 2024 16:03:45 +0200 Subject: [PATCH 18/31] fix(writer): Fixes matplotlib error on headless machines --- blenderproc/python/writer/GifWriterUtility.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blenderproc/python/writer/GifWriterUtility.py b/blenderproc/python/writer/GifWriterUtility.py index 396f93983..41a87774b 100644 --- a/blenderproc/python/writer/GifWriterUtility.py +++ b/blenderproc/python/writer/GifWriterUtility.py @@ -6,6 +6,9 @@ import bpy import numpy as np from PIL import Image +# Make sure we use headless matplotlib +import matplotlib +matplotlib.use("agg") from blenderproc.scripts.visHdf5Files import vis_data from blenderproc.python.utility.Utility import Utility From 3c1be7b0389333bc8b1e6c18b03c0f1091eceb14 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Mon, 19 Aug 2024 17:51:32 +0200 Subject: [PATCH 19/31] feat(blender): assign the root collection after cleaning up data blocks So the 'bpy.context.collection' can be used afterwards. This worked in Blender 4.1, but in 4.2 deleting the collection objects doesn't make the root collection as the active in context. --- blenderproc/python/utility/Initializer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blenderproc/python/utility/Initializer.py b/blenderproc/python/utility/Initializer.py index b1e58549c..639c76ce4 100644 --- a/blenderproc/python/utility/Initializer.py +++ b/blenderproc/python/utility/Initializer.py @@ -74,6 +74,10 @@ def clean_up(clean_up_camera: bool = False): _Initializer.remove_all_data(clean_up_camera) _Initializer.remove_custom_properties() + # Make the default 'LayerCollection' as active, so bpy.context.collection points to it. + # This is the 'Scene Collection' in the outliner. + bpy.context.view_layer.active_layer_collection = bpy.context.view_layer.layer_collection + # Create new world new_world = bpy.data.worlds.new("World") bpy.context.scene.world = new_world From c24a8e5d412d33bdc7ba27f64102c7128a347d15 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Tue, 27 Aug 2024 11:57:10 +0200 Subject: [PATCH 20/31] fix(mesh): convert deg to rad when assigning to the shade smooth modifier --- blenderproc/python/types/MeshObjectUtility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 80a708545..3043b46c2 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -545,7 +545,7 @@ def add_auto_smooth_modifier(self, angle: float = 30.0): modifier.node_group = existing_node_group modifier = self.blender_obj.modifiers["Smooth by Angle"] - modifier["Input_1"] = float(angle) + modifier["Input_1"] = np.deg2rad(float(angle)) def mesh_as_trimesh(self) -> Trimesh: """ Returns a trimesh.Trimesh instance of the MeshObject. From a37b4f006e495cbc5d01a0eba921aee7192632ff Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Tue, 27 Aug 2024 14:43:02 +0200 Subject: [PATCH 21/31] feat(blender): bump to 4.2.1 LTS --- blenderproc/python/utility/InstallUtility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/utility/InstallUtility.py b/blenderproc/python/utility/InstallUtility.py index 5f202f284..645d06a8a 100644 --- a/blenderproc/python/utility/InstallUtility.py +++ b/blenderproc/python/utility/InstallUtility.py @@ -78,9 +78,9 @@ def make_sure_blender_is_installed(custom_blender_path: Optional[str], blender_i blender_install_path = "blender" # Determine configured version - # right now only support blender-4.2.0 + # right now only support blender-4.2.1 major_version = "4.2" - minor_version = "0" + minor_version = "1" blender_version = f"blender-{major_version}.{minor_version}" if platform in ["linux", "linux2"]: blender_version += "-linux-x64" From 4645722cfb83a5cd9d985403cea021dd97e6ee61 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Tue, 27 Aug 2024 17:27:29 +0200 Subject: [PATCH 22/31] fix(amass): Fixes subsurface shader --- blenderproc/python/loader/AMASSLoader.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/loader/AMASSLoader.py b/blenderproc/python/loader/AMASSLoader.py index c7c2cbbe5..cbed12cf4 100644 --- a/blenderproc/python/loader/AMASSLoader.py +++ b/blenderproc/python/loader/AMASSLoader.py @@ -285,9 +285,12 @@ def correct_materials(objects: List[MeshObject]): skin_tone_fac = random.uniform(0.0, 1) skin_tone_rgb = [value * skin_tone_fac for value in skin_tone_rgb] principled_bsdf.inputs["Base Color"].default_value = mathutils.Vector([*skin_tone_rgb, 1.0]) - principled_bsdf.inputs["Subsurface Weight"].default_value = 0.2 + + principled_bsdf.subsurface_method = "RANDOM_WALK_SKIN" + principled_bsdf.inputs["Subsurface Weight"].default_value = 1 + principled_bsdf.inputs["Subsurface Scale"].default_value = 0.2 principled_bsdf.inputs["Subsurface Radius"].default_value = mathutils.Vector([1.0, 0.2, 0.1]) - principled_bsdf.inputs["IOR"].default_value = 2.5 + principled_bsdf.inputs["Subsurface IOR"].default_value = 2.5 # darker skin looks better when made less specular principled_bsdf.inputs["Specular IOR Level"].default_value = np.mean(skin_tone_rgb) / 255.0 From f99fb3236cfc8592e5590b71cabc65440a8c9896 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 29 Aug 2024 11:10:53 +0200 Subject: [PATCH 23/31] fix(ObjectLoader): don't validate meshes when importing obj This should fix the broken normals. --- blenderproc/python/loader/ObjectLoader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blenderproc/python/loader/ObjectLoader.py b/blenderproc/python/loader/ObjectLoader.py index 12ab1bdcc..db67a96e0 100644 --- a/blenderproc/python/loader/ObjectLoader.py +++ b/blenderproc/python/loader/ObjectLoader.py @@ -41,7 +41,7 @@ def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]] previously_selected_objects = bpy.context.selected_objects if filepath.endswith(".obj"): # load an .obj file: - bpy.ops.wm.obj_import(filepath=filepath, **kwargs) + bpy.ops.wm.obj_import(filepath=filepath, **kwargs, validate_meshes=False) elif filepath.endswith(".ply"): PLY_TEXTURE_FILE_COMMENT = "comment TextureFile " model_name = os.path.basename(filepath) From 4cacdbeafafe3b64d42892351c4056873fbbc270 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Thu, 29 Aug 2024 15:11:25 +0200 Subject: [PATCH 24/31] fix(ply): Removes incorrect custom split normals from bop/replica objects loaded from ply --- blenderproc/python/loader/BopLoader.py | 4 ++++ blenderproc/python/loader/ReplicaLoader.py | 3 +++ blenderproc/python/types/MeshObjectUtility.py | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/blenderproc/python/loader/BopLoader.py b/blenderproc/python/loader/BopLoader.py index 35de5eb00..e518ec34a 100644 --- a/blenderproc/python/loader/BopLoader.py +++ b/blenderproc/python/loader/BopLoader.py @@ -347,6 +347,10 @@ def load_mesh(obj_id: int, model_p: dict, bop_dataset_name: str, scale: float = # if the object was not previously loaded - load it, if duplication is allowed - duplicate it duplicated = model_path in _BopLoader.CACHED_OBJECTS objs = load_obj(model_path, cached_objects=_BopLoader.CACHED_OBJECTS) + # Bop objects comes with incorrect custom normals, so remove them + for obj in objs: + obj.clear_custom_splitnormals() + assert ( len(objs) == 1 ), f"Loading object from '{model_path}' returned more than one mesh" diff --git a/blenderproc/python/loader/ReplicaLoader.py b/blenderproc/python/loader/ReplicaLoader.py index 4a75a0189..0309b1a16 100644 --- a/blenderproc/python/loader/ReplicaLoader.py +++ b/blenderproc/python/loader/ReplicaLoader.py @@ -135,6 +135,9 @@ def load_replica(data_path: str, data_set_name: str, use_smooth_shading: bool = """ file_path = os.path.join(data_path, data_set_name, 'mesh.ply') loaded_objects = load_obj(file_path) + # Replica comes with incorrect custom normals, so remove them + for obj in loaded_objects: + obj.clear_custom_splitnormals() if use_smooth_shading: for obj in loaded_objects: diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 3043b46c2..2ffe3181e 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -575,6 +575,12 @@ def mesh_as_trimesh(self) -> Trimesh: return Trimesh(vertices=verts, faces=faces) + def clear_custom_splitnormals(self) -> None: + """ Removes custom split normals which might exist after importing the object from file. """ + + with bpy.context.temp_override(object=self.blender_obj): + bpy.ops.mesh.customdata_custom_splitnormals_clear() + def create_from_blender_mesh(blender_mesh: bpy.types.Mesh, object_name: str = None) -> "MeshObject": """ Creates a new Mesh object using the given blender mesh. From b0e01b800dc93b69db24d394dc42f836af672e01 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Wed, 4 Sep 2024 12:06:26 +0200 Subject: [PATCH 25/31] fix(material): Adjusts displacement scale to fit blender 4.2 --- blenderproc/python/material/MaterialLoaderUtility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blenderproc/python/material/MaterialLoaderUtility.py b/blenderproc/python/material/MaterialLoaderUtility.py index 53fb1d9d1..40d3f17d7 100644 --- a/blenderproc/python/material/MaterialLoaderUtility.py +++ b/blenderproc/python/material/MaterialLoaderUtility.py @@ -347,7 +347,7 @@ def add_displacement(nodes: bpy.types.Nodes, links: bpy.types.NodeLinks, displac _y_texture_node * -4) displacement_node = nodes.new("ShaderNodeDisplacement") displacement_node.inputs["Midlevel"].default_value = 0.5 - displacement_node.inputs["Scale"].default_value = 0.15 + displacement_node.inputs["Scale"].default_value = 0.03 displacement_node.location.x = _x_texture_node * 0.5 displacement_node.location.y = _y_texture_node * -4 links.new(displacement_texture.outputs["Color"], displacement_node.inputs["Height"]) From eeaf5efae1f6b234d4d036b18db23f863473f460 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Fri, 6 Sep 2024 17:52:38 +0200 Subject: [PATCH 26/31] fix(loader): Allow overwriting validate_meshes in ObjLoader --- blenderproc/python/loader/ObjectLoader.py | 5 ++++- blenderproc/python/loader/ShapeNetLoader.py | 6 ++++-- examples/datasets/shapenet/main.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/blenderproc/python/loader/ObjectLoader.py b/blenderproc/python/loader/ObjectLoader.py index db67a96e0..c3c21e9fc 100644 --- a/blenderproc/python/loader/ObjectLoader.py +++ b/blenderproc/python/loader/ObjectLoader.py @@ -40,8 +40,11 @@ def load_obj(filepath: str, cached_objects: Optional[Dict[str, List[MeshObject]] # save all selected objects previously_selected_objects = bpy.context.selected_objects if filepath.endswith(".obj"): + # Set validate_meshes to False per default to be backwards compatible + if "validate_meshes" not in kwargs: + kwargs["validate_meshes"] = False # load an .obj file: - bpy.ops.wm.obj_import(filepath=filepath, **kwargs, validate_meshes=False) + bpy.ops.wm.obj_import(filepath=filepath, **kwargs) elif filepath.endswith(".ply"): PLY_TEXTURE_FILE_COMMENT = "comment TextureFile " model_name = os.path.basename(filepath) diff --git a/blenderproc/python/loader/ShapeNetLoader.py b/blenderproc/python/loader/ShapeNetLoader.py index c9a52ed95..f952d7a41 100644 --- a/blenderproc/python/loader/ShapeNetLoader.py +++ b/blenderproc/python/loader/ShapeNetLoader.py @@ -14,7 +14,7 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", - move_object_origin: bool = True) -> MeshObject: + move_object_origin: bool = True, validate_meshes: bool = False) -> MeshObject: """ This loads an object from ShapeNet based on the given synset_id, which specifies the category of objects to use. From these objects one is randomly sampled and loaded. @@ -30,6 +30,8 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", :param move_object_origin: Moves the object center to the bottom of the bounding box in Z direction and also in the middle of the X and Y plane, this does not change the `.location` of the object. Default: True + :param validate_meshes: If set to True, imported meshed will be validated and corrected. + This might help for some ShapeNet objects to e.g. remove duplicate faces. :return: The loaded mesh object. """ data_path = resolve_path(data_path) @@ -39,7 +41,7 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", taxonomy_file_path, data_path) selected_obj = random.choice(files_with_fitting_synset) # with the new version the textures are all wrong - loaded_objects = load_obj(selected_obj) + loaded_objects = load_obj(selected_obj, validate_meshes=validate_meshes) # In shapenet every .obj file only contains one object, make sure that is the case if len(loaded_objects) != 1: diff --git a/examples/datasets/shapenet/main.py b/examples/datasets/shapenet/main.py index bd6f11f28..bf22afa3a 100644 --- a/examples/datasets/shapenet/main.py +++ b/examples/datasets/shapenet/main.py @@ -9,7 +9,7 @@ bproc.init() # load the ShapeNet object into the scene -shapenet_obj = bproc.loader.load_shapenet(args.shapenet_path, used_synset_id="02691156", used_source_id="10155655850468db78d106ce0a280f87") +shapenet_obj = bproc.loader.load_shapenet(args.shapenet_path, used_synset_id="02691156", used_source_id="10155655850468db78d106ce0a280f87", validate_meshes=True) # define a light and set its location and energy level light = bproc.types.Light() From 61e2cf7586cafcd2ab87444ad5dd32bc68f1cfec Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Mon, 9 Sep 2024 10:44:22 +0200 Subject: [PATCH 27/31] fix(shapenet): Enables mesh validation per default for shapenet objects --- blenderproc/python/loader/ShapeNetLoader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blenderproc/python/loader/ShapeNetLoader.py b/blenderproc/python/loader/ShapeNetLoader.py index f952d7a41..9f3df709f 100644 --- a/blenderproc/python/loader/ShapeNetLoader.py +++ b/blenderproc/python/loader/ShapeNetLoader.py @@ -14,7 +14,7 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", - move_object_origin: bool = True, validate_meshes: bool = False) -> MeshObject: + move_object_origin: bool = True, validate_meshes: bool = True) -> MeshObject: """ This loads an object from ShapeNet based on the given synset_id, which specifies the category of objects to use. From these objects one is randomly sampled and loaded. @@ -32,6 +32,7 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", Default: True :param validate_meshes: If set to True, imported meshed will be validated and corrected. This might help for some ShapeNet objects to e.g. remove duplicate faces. + However, it might lead to the texturing being destroyed. :return: The loaded mesh object. """ data_path = resolve_path(data_path) From 95ba9f3e77ae788662d76e5625d427d00e73a3c3 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Mon, 9 Sep 2024 11:44:45 +0200 Subject: [PATCH 28/31] fix(physics): Use surface area to compute center of mass per default --- blenderproc/python/loader/ShapeNetLoader.py | 2 +- blenderproc/python/object/PhysicsSimulation.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/blenderproc/python/loader/ShapeNetLoader.py b/blenderproc/python/loader/ShapeNetLoader.py index 9f3df709f..f5490df19 100644 --- a/blenderproc/python/loader/ShapeNetLoader.py +++ b/blenderproc/python/loader/ShapeNetLoader.py @@ -14,7 +14,7 @@ def load_shapenet(data_path: str, used_synset_id: str, used_source_id: str = "", - move_object_origin: bool = True, validate_meshes: bool = True) -> MeshObject: + move_object_origin: bool = True, validate_meshes: bool = False) -> MeshObject: """ This loads an object from ShapeNet based on the given synset_id, which specifies the category of objects to use. From these objects one is randomly sampled and loaded. diff --git a/blenderproc/python/object/PhysicsSimulation.py b/blenderproc/python/object/PhysicsSimulation.py index 516b34189..804f94e8b 100644 --- a/blenderproc/python/object/PhysicsSimulation.py +++ b/blenderproc/python/object/PhysicsSimulation.py @@ -13,7 +13,7 @@ def simulate_physics_and_fix_final_poses(min_simulation_time: float = 4.0, max_s check_object_interval: float = 2.0, object_stopped_location_threshold: float = 0.01, object_stopped_rotation_threshold: float = 0.1, substeps_per_frame: int = 10, - solver_iters: int = 10, verbose: bool = False): + solver_iters: int = 10, verbose: bool = False, use_volume_com: bool = False): """ Simulates the current scene and in the end fixes the final poses of all active objects. The simulation is run for at least `min_simulation_time` seconds and at a maximum `max_simulation_time` seconds. @@ -36,6 +36,8 @@ def simulate_physics_and_fix_final_poses(min_simulation_time: float = 4.0, max_s :param substeps_per_frame: Number of simulation steps taken per frame. :param solver_iters: Number of constraint solver iterations made per simulation step. :param verbose: If True, more details during the physics simulation are printed. + :param use_volume_com: If True, the center of mass will be calculated by using the object volume. + This is more accurate than using the surface area (default), but requires a watertight mesh. """ # Undo changes made in the simulation like origin adjustment and persisting the object's scale with UndoAfterExecution(): @@ -43,7 +45,7 @@ def simulate_physics_and_fix_final_poses(min_simulation_time: float = 4.0, max_s obj_poses_before_sim = _PhysicsSimulation.get_pose() origin_shifts = simulate_physics(min_simulation_time, max_simulation_time, check_object_interval, object_stopped_location_threshold, object_stopped_rotation_threshold, - substeps_per_frame, solver_iters, verbose) + substeps_per_frame, solver_iters, verbose, use_volume_com) obj_poses_after_sim = _PhysicsSimulation.get_pose() # Make sure to remove the simulation cache as we are only interested in the final poses @@ -77,7 +79,7 @@ def simulate_physics_and_fix_final_poses(min_simulation_time: float = 4.0, max_s def simulate_physics(min_simulation_time: float = 4.0, max_simulation_time: float = 40.0, check_object_interval: float = 2.0, object_stopped_location_threshold: float = 0.01, object_stopped_rotation_threshold: float = 0.1, substeps_per_frame: int = 10, - solver_iters: int = 10, verbose: bool = False) -> dict: + solver_iters: int = 10, verbose: bool = False, use_volume_com: bool = False) -> dict: """ Simulates the current scene. The simulation is run for at least `min_simulation_time` seconds and at a maximum `max_simulation_time` seconds. @@ -101,6 +103,8 @@ def simulate_physics(min_simulation_time: float = 4.0, max_simulation_time: floa :param substeps_per_frame: Number of simulation steps taken per frame. :param solver_iters: Number of constraint solver iterations made per simulation step. :param verbose: If True, more details during the physics simulation are printed. + :param use_volume_com: If True, the center of mass will be calculated by using the object volume. + This is more accurate than using the surface area (default), but requires a watertight mesh. :return: A dict containing for every active object the shift that was added to their origins. """ # Shift the origin of all objects to their center of mass to make the simulation more realistic @@ -108,7 +112,7 @@ def simulate_physics(min_simulation_time: float = 4.0, max_simulation_time: floa for obj in get_all_mesh_objects(): if obj.has_rigidbody_enabled(): prev_origin = obj.get_origin() - new_origin = obj.set_origin(mode="CENTER_OF_VOLUME") + new_origin = obj.set_origin(mode="ORIGIN_CENTER_OF_VOLUME" if use_volume_com else "CENTER_OF_MASS") origin_shift[obj.get_name()] = new_origin - prev_origin # Persist mesh scaling as having a scale != 1 can make the simulation unstable From 593d344728bdd6098883107eea285597ec10a9ca Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 12 Sep 2024 10:58:44 +0200 Subject: [PATCH 29/31] doc(mesh): document 'angle' parameter in 'add_auto_smooth_modifier' --- blenderproc/python/types/MeshObjectUtility.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index 2ffe3181e..c05000413 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -513,6 +513,8 @@ def add_auto_smooth_modifier(self, angle: float = 30.0): """ Adds the 'Smooth by Angle' geometry nodes modifier. This replaces the 'Auto Smooth' behavior available in Blender before 4.1. + + :param angle: Maximum angle (in degrees) between face normals that will be considered as smooth. """ # The bpy.ops.object.modifier_add_node_group doesn't work in background mode :( # So we load the node group and create the modifier ourselves. From 275141e4a0571c62803d04e51fcfb97bc2c82907 Mon Sep 17 00:00:00 2001 From: Zdenek Dolezal Date: Thu, 12 Sep 2024 11:08:54 +0200 Subject: [PATCH 30/31] refactor(render): move manipulating 'view_transform' to 'set_output_format' --- blenderproc/python/renderer/RendererUtility.py | 12 +++++------- blenderproc/python/utility/DefaultConfig.py | 1 + blenderproc/python/utility/Initializer.py | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/blenderproc/python/renderer/RendererUtility.py b/blenderproc/python/renderer/RendererUtility.py index 381e707d8..5ef699b0e 100644 --- a/blenderproc/python/renderer/RendererUtility.py +++ b/blenderproc/python/renderer/RendererUtility.py @@ -654,7 +654,6 @@ def _render_progress_bar(pipe_out: int, pipe_in: int, stdout: IO, total_frames: def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_key: Optional[str] = "colors", load_keys: Optional[Set[str]] = None, return_data: bool = True, keys_with_alpha_channel: Optional[Set[str]] = None, - view_transform: str = "Filmic", verbose: bool = False) -> Dict[str, Union[np.ndarray, List[np.ndarray]]]: """ Render all frames. @@ -667,7 +666,6 @@ def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_k :param load_keys: Set of output keys to load when available :param return_data: Whether to load and return generated data. :param keys_with_alpha_channel: A set containing all keys whose alpha channels should be loaded. - :param view_transform: Determines the view transform to use for rendering. :param verbose: If True, more details about the rendering process are printed. :return: dict of lists of raw renderer output. Keys can be 'distance', 'colors', 'normals' """ @@ -677,10 +675,6 @@ def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_k load_keys = {'colors', 'distance', 'normals', 'diffuse', 'depth', 'segmap'} keys_with_alpha_channel = {'colors'} if bpy.context.scene.render.film_transparent else None - if view_transform not in {"AgX", "Standard", "Filmic", "Filmic Log", "False Color", "Raw"}: - raise ValueError(f"Unknown view transform {view_transform}") - - bpy.context.scene.view_settings.view_transform = view_transform if output_key is not None: Utility.add_output_entry({ "key": output_key, @@ -736,7 +730,8 @@ def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_k def set_output_format(file_format: Optional[str] = None, color_depth: Optional[int] = None, - enable_transparency: Optional[bool] = None, jpg_quality: Optional[int] = None): + enable_transparency: Optional[bool] = None, jpg_quality: Optional[int] = None, + view_transform: Optional[str] = None): """ Sets the output format to use for rendering. Default values defined in DefaultConfig.py. :param file_format: The file format to use, e.q. "PNG", "JPEG" or "OPEN_EXR". @@ -744,6 +739,7 @@ def set_output_format(file_format: Optional[str] = None, color_depth: Optional[i :param enable_transparency: If true, the output will contain a alpha channel and the background will be set transparent. :param jpg_quality: The quality to use, if file format is set to "JPEG". + :param view_transform: View transform of the rendered output. """ if enable_transparency is not None: # In case a previous renderer changed these settings @@ -758,6 +754,8 @@ def set_output_format(file_format: Optional[str] = None, color_depth: Optional[i if jpg_quality is not None: # only influences jpg quality bpy.context.scene.render.image_settings.quality = jpg_quality + if view_transform is not None: + bpy.context.scene.view_settings.view_transform = view_transform def enable_motion_blur(motion_blur_length: float = 0.5, rolling_shutter_type: str = "NONE", diff --git a/blenderproc/python/utility/DefaultConfig.py b/blenderproc/python/utility/DefaultConfig.py index 441016b9a..669096db2 100644 --- a/blenderproc/python/utility/DefaultConfig.py +++ b/blenderproc/python/utility/DefaultConfig.py @@ -41,6 +41,7 @@ class DefaultConfig: volume_bounces = 0 antialiasing_distance_max = 10000 world_background = [0.05, 0.05, 0.05] + view_transform = "Filmic" # Setup default_pip_packages = ["wheel", "pyyaml==6.0.1", "imageio==2.34.1", "gitpython==3.1.43", diff --git a/blenderproc/python/utility/Initializer.py b/blenderproc/python/utility/Initializer.py index 639c76ce4..a5d868fc6 100644 --- a/blenderproc/python/utility/Initializer.py +++ b/blenderproc/python/utility/Initializer.py @@ -138,7 +138,8 @@ def set_default_parameters(): RendererUtility.set_output_format(DefaultConfig.file_format, DefaultConfig.color_depth, DefaultConfig.enable_transparency, - DefaultConfig.jpg_quality) + DefaultConfig.jpg_quality, + DefaultConfig.view_transform) @staticmethod def remove_all_data(remove_camera: bool = True): From 01c8a12b9eacce8c5ebeaa4ab3840244741ac309 Mon Sep 17 00:00:00 2001 From: Dominik Winkelbauer Date: Thu, 12 Sep 2024 11:28:44 +0200 Subject: [PATCH 31/31] fix(mesh): Makes smooth modifier working on mac --- blenderproc/python/types/MeshObjectUtility.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blenderproc/python/types/MeshObjectUtility.py b/blenderproc/python/types/MeshObjectUtility.py index c05000413..f6e114d2b 100644 --- a/blenderproc/python/types/MeshObjectUtility.py +++ b/blenderproc/python/types/MeshObjectUtility.py @@ -17,6 +17,7 @@ from blenderproc.python.utility.BlenderUtility import get_all_blender_mesh_objects from blenderproc.python.types.MaterialUtility import Material from blenderproc.python.material import MaterialLoaderUtility +from blenderproc.python.utility.SetupUtility import SetupUtility if platform != "win32": # this is only supported under linux and macOS, the import itself already doesn't work under windows @@ -520,8 +521,9 @@ def add_auto_smooth_modifier(self, angle: float = 30.0): # So we load the node group and create the modifier ourselves. # Known issue: https://projects.blender.org/blender/blender/issues/117399 - # The datafiles are expected to be in the same folder as the 'blender_binary/version'. - path = Path(bpy.app.binary_path).parent / "4.2" / "datafiles" / "assets" / "geometry_nodes" / "smooth_by_angle.blend" + # The datafiles are expected to be in the same folder relative to blender's python binary. + python_bin = SetupUtility.determine_python_paths(None, None)[0] + path = Path(python_bin).parent.parent.parent / "datafiles" / "assets" / "geometry_nodes" / "smooth_by_angle.blend" if not path.exists(): raise RuntimeError(f"Could not find the path to the 'ESSENTIALS' asset folder expected at {path}")