From d850b9bb7b1eef555abbd6f35be06a7476956f98 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 11 Apr 2024 07:48:14 -0700 Subject: [PATCH 1/5] Fix for RMAN-22135 - FOV in viewport not updating correctly. This situation happened when you IPR in the viewport, but there is no camera in the scene. * we were not updating the projection when the resolution change (i.e.: the user has resized windows) * when there is no camera in the scene, always use HORIZONTAL fit when calculating the aspectratio --- rfb_utils/camera_utils.py | 6 +++++- rman_scene_sync.py | 5 ++--- rman_translators/rman_camera_translator.py | 16 +++++++++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/rfb_utils/camera_utils.py b/rfb_utils/camera_utils.py index 9cfc9bbc..327bd68a 100644 --- a/rfb_utils/camera_utils.py +++ b/rfb_utils/camera_utils.py @@ -14,7 +14,11 @@ def render_get_aspect_(r, camera=None, x=-1, y=-1): xratio = xres * r.pixel_aspect_x / 200.0 yratio = yres * r.pixel_aspect_y / 200.0 - if camera is None or camera.type != 'PERSP': + if camera is None: + # If we don't have a camera, always use HORIZONTAL + # This is the viewport render case, but there's no camera in the scene + fit = 'HORIZONTAL' + elif camera.type != 'PERSP': fit = 'AUTO' else: fit = camera.sensor_fit diff --git a/rman_scene_sync.py b/rman_scene_sync.py index f7889884..6bc809de 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -86,9 +86,8 @@ def update_view(self, context, depsgraph): translator = self.rman_scene.rman_translators['CAMERA'] with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): if self.rman_scene.is_viewport_render: - ob = translator.update_viewport_resolution(rman_sg_camera) - if ob: - translator.update_viewport_cam(ob, rman_sg_camera, force_update=True) + if translator.update_viewport_resolution(rman_sg_camera): + translator.update_viewport_cam(None, rman_sg_camera, force_update=True) translator.update_transform(None, rman_sg_camera) else: translator.update_transform(camera, rman_sg_camera) diff --git a/rman_translators/rman_camera_translator.py b/rman_translators/rman_camera_translator.py index 8b98c3ee..5bb82b48 100644 --- a/rman_translators/rman_camera_translator.py +++ b/rman_translators/rman_camera_translator.py @@ -83,8 +83,8 @@ def _export_viewport_cam(self, db_name=""): rman_sg_camera = RmanSgCamera(self.rman_scene, sg_group, db_name) rman_sg_camera.sg_camera_node = self.rman_scene.sg_scene.CreateCamera('%s-CAMERA' % db_name) sg_group.AddChild(rman_sg_camera.sg_camera_node) - ob = self.update_viewport_resolution(rman_sg_camera) - self.update_viewport_cam(ob, rman_sg_camera) + self.update_viewport_resolution(rman_sg_camera) + self.update_viewport_cam(None, rman_sg_camera) self._set_orientation(rman_sg_camera) self._update_viewport_transform(rman_sg_camera) return rman_sg_camera @@ -398,8 +398,8 @@ def update_viewport_resolution(self, rman_sg_camera): options.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_CropWindow, crop_window, 4) self.rman_scene.sg_scene.SetOptions(options) rman_sg_camera.sg_camera_node.SetProperties(prop) - return ob - return None + return True + return False def update_viewport_cam(self, ob, rman_sg_camera, force_update=False): region = self.rman_scene.context.region @@ -419,6 +419,10 @@ def update_viewport_cam(self, ob, rman_sg_camera, force_update=False): bl_cam_props = deepcopy(rman_sg_camera.bl_cam_props) if rman_sg_camera.bl_cam_props.view_perspective == 'CAMERA': + if ob is None: + ob = self.rman_scene.bl_scene.camera + if self.rman_scene.context.space_data.use_local_camera: + ob = self.rman_scene.context.space_data.camera ob = ob.original cam = ob.data rman_sg_camera.bl_camera = ob @@ -478,6 +482,8 @@ def update_viewport_cam(self, ob, rman_sg_camera, force_update=False): rman_sg_camera.rman_focus_object = None elif rman_sg_camera.bl_cam_props.view_perspective == 'PERSP': + if ob is None: + ob = self.rman_scene.context.space_data.camera cam = None if ob: cam = ob.data @@ -494,7 +500,7 @@ def update_viewport_cam(self, ob, rman_sg_camera, force_update=False): region_data = self.rman_scene.context.region_data vmat_inv = region_data.view_matrix.inverted() pmat = region_data.perspective_matrix @ vmat_inv - fov = 360.0 * math.atan(1.0/pmat[1][1]) / math.pi + fov = 2.0 * math.atan(1.0/pmat[1][1]) * 180.0 / math.pi bl_cam_props.rman_fov = fov From 2aa56c1db3fdfe053b5624c202215234cbc9bbc2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 17 Apr 2024 07:23:49 -0700 Subject: [PATCH 2/5] Fix for CUSP-8102 For some reason, if the scene wasn't saved our 'use_renderman_node' property on the world object wasn't getting set for an IPR. Since we only really want to know if the world object is using nodes, just check the 'use_nodes' property. --- rfb_utils/shadergraph_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfb_utils/shadergraph_utils.py b/rfb_utils/shadergraph_utils.py index 5d2d9fb0..ad565cfa 100644 --- a/rfb_utils/shadergraph_utils.py +++ b/rfb_utils/shadergraph_utils.py @@ -793,7 +793,7 @@ def find_integrator_node(world): (RendermanIntegratorNode) - the integrator ShadingNode ''' rm = world.renderman - if not world.renderman.use_renderman_node: + if not world.use_nodes: return None output = find_node(world, 'RendermanIntegratorsOutputNode') @@ -814,7 +814,7 @@ def find_displayfilter_nodes(world): (list) - list of display filter nodes ''' df_nodes = [] - if not world.renderman.use_renderman_node: + if not world.use_nodes: return df_nodes output = find_node(world, 'RendermanDisplayfiltersOutputNode') @@ -836,7 +836,7 @@ def find_samplefilter_nodes(world): (list) - list of sample filter nodes ''' sf_nodes = [] - if not world.renderman.use_renderman_node: + if not world.use_nodes: return sf_nodes output = find_node(world, 'RendermanSamplefiltersOutputNode') From 54859cea858d0a433cb526545992dbee2b8bd685 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 22 Apr 2024 11:09:50 -0700 Subject: [PATCH 3/5] Fix for RMAN-22194 Geometry nodes that instance multipe objects seem to present itself as one big mesh. When dealing with multiple material meshes, we always assumed that material index 0 always exists. Now, look for the minimum material index and use that as our base mesh, not 0. --- rman_render.py | 4 ++++ rman_translators/rman_mesh_translator.py | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rman_render.py b/rman_render.py index d6d81ef4..7a166fa3 100644 --- a/rman_render.py +++ b/rman_render.py @@ -1246,6 +1246,8 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.rman_running = True bl_scene = context.scene + if self._do_prman_render_begin(): + return False if export_all_frames: original_frame = bl_scene.frame_current rfb_log().debug("Writing to RIB...") @@ -1298,6 +1300,8 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.rman_scene.reset() self.rman_running = False + self.del_bl_engine() + self._do_prman_render_end() return True def stop_render(self, stop_draw_thread=True): diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index 395ba1de..21eebae1 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -505,9 +505,9 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): if rman_sg_mesh.is_multi_material: material_ids = _get_material_ids(ob, mesh) i = 1 - for mat_id, faces in \ - _get_mats_faces_(nverts, material_ids).items(): - + mat_faces_dict = _get_mats_faces_(nverts, material_ids) + min_idx = min(mat_faces_dict.keys()) # find the minimun material index + for mat_id, faces in mat_faces_dict.items(): # If the face has a mat index that is higher than the number of # material slots, use the last material. This is what # Eevee/Cycles does. @@ -519,7 +519,7 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): continue sg_material = self.rman_scene.rman_materials.get(mat.original, None) - if mat_id == 0: + if mat_id == min_idx: primvar.SetIntegerArray(self.rman_scene.rman.Tokens.Rix.k_shade_faceset, faces, len(faces)) scenegraph_utils.set_material(sg_node, sg_material.sg_node, sg_material, mat=mat, ob=ob) else: From 09f8bcb7e8731de181aadd007a2314c9718e489c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 22 Apr 2024 11:21:29 -0700 Subject: [PATCH 4/5] Fix one of our unit tests. Meshes are slightly different in Blender 4.1. --- rfb_unittests/test_geo.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rfb_unittests/test_geo.py b/rfb_unittests/test_geo.py index 7d31cdb0..08bda478 100644 --- a/rfb_unittests/test_geo.py +++ b/rfb_unittests/test_geo.py @@ -1,6 +1,7 @@ import unittest import bpy from ..rfb_utils import mesh_utils +from ..rman_constants import BLENDER_41 class GeoTest(unittest.TestCase): @@ -20,7 +21,10 @@ def test_eq(a, b, msg=None): nverts = [4, 4, 4, 4, 4, 4] verts = [0, 1, 3, 2, 2, 3, 7, 6, 6, 7, 5, 4, 4, 5, 1, 0, 2, 6, 4, 0, 7, 3, 1, 5] P = [[-1.0, -1.0, -1.0], [-1.0, -1.0, 1.0], [-1.0, 1.0, -1.0], [-1.0, 1.0, 1.0], [1.0, -1.0, -1.0], [1.0, -1.0, 1.0], [1.0, 1.0, -1.0], [1.0, 1.0, 1.0]] - N = [[-1.0, -0.0, 0.0], [0.0, 1.0, 0.0], [1.0, -0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [0.0, -0.0, 1.0]] + if BLENDER_41: + N = [[-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]] + else: + N = [[-1.0, -0.0, 0.0], [0.0, 1.0, 0.0], [1.0, -0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [0.0, -0.0, 1.0]] mesh = mesh_utils.RmanMesh(nverts, verts, P, N) From d2730ee29452b1c9acf335aa57a95e063d048a8d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 23 Apr 2024 07:51:58 -0700 Subject: [PATCH 5/5] Fix for RMAN-22201 Refreshing PxrOSL nodes was failing * double check the length of color parameters. The length for the default_value for our color sockets is 4, so make sure to increase/decrease the length when necessary * cannot deepcopy Blender's bpy_prop_array, so manually create a list and copy the values. * also bump the addon version and add a changelog for 26.1. --- __init__.py | 2 +- changelog.txt | 10 ++++++++++ rman_bl_nodes/rman_bl_nodes_shaders.py | 24 +++++++++++++++++++----- rman_bl_nodes/rman_bl_nodes_sockets.py | 2 +- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/__init__.py b/__init__.py index 1aa8bae0..97c3b6c6 100644 --- a/__init__.py +++ b/__init__.py @@ -30,7 +30,7 @@ bl_info = { "name": "RenderMan For Blender", "author": "Pixar", - "version": (26, 0, 0), + "version": (26, 1, 0), "blender": (2, 93, 0), "location": "Render Properties > Render Engine > RenderMan", "description": "RenderMan 26 integration", diff --git a/changelog.txt b/changelog.txt index c9450948..a3b9d6af 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,13 @@ +v26.1, April 24, 2024 + +Bug Fixes: +* Fixed bugs related to refreshing an OSL shader via the PxrOSL shading node +* Fixed a bug that caused the field of view to no be calculated correctly when +using an orthographic camera +* Fixed a bug that caused integrators to use the wrong settings during IPR +* Fixed a bug that caused instances created via geometry nodes to have the +wrong material attached + v26.0, April 02, 2024 New Features: diff --git a/rman_bl_nodes/rman_bl_nodes_shaders.py b/rman_bl_nodes/rman_bl_nodes_shaders.py index 7e89075f..c59dd3ee 100644 --- a/rman_bl_nodes/rman_bl_nodes_shaders.py +++ b/rman_bl_nodes/rman_bl_nodes_shaders.py @@ -7,7 +7,7 @@ from ..rfb_utils.property_utils import BlPropInfo, __LOBES_ENABLE_PARAMS__ from ..rfb_utils import filepath_utils from ..rman_config import __RFB_CONFIG_DICT__ -from ..rman_constants import RFB_FLOAT3, RFB_SHADER_ALLOWED_CONNECTIONS +from ..rman_constants import RFB_FLOAT3, RFB_SHADER_ALLOWED_CONNECTIONS, __RMAN_SOCKET_MAP__ from .. import rman_bl_nodes from .. import rfb_icons from .. import rman_render @@ -707,7 +707,14 @@ def setOslProps(self, prop_names, shader_meta): if socket.is_linked: links[input_name] = {"from_node": socket.links[0].from_node, "from_socket": socket.links[0].from_socket} else: - values[input_name] = deepcopy(socket.default_value) + if isinstance(socket.default_value, bpy.types.bpy_prop_array): + # deecopy seems to fail on bpy_prop_array types, so manually copy + val = list() + for v in socket.default_value: + val.append(v) + values[input_name] = val + else: + values[input_name] = deepcopy(socket.default_value) for input_name, socket in self.outputs.items(): if socket.is_linked: links[input_name] = {"from_node": socket.links[0].to_node, "from_socket": socket.links[0].to_socket} @@ -722,7 +729,7 @@ def setOslProps(self, prop_names, shader_meta): prop_type = shader_meta[prop_name]["type"] if shader_meta[prop_name]["IO"] == "out": socket = self.outputs.new( - rman_socket_utils.__RMAN_SOCKET_MAP__[prop_type], prop_name) + __RMAN_SOCKET_MAP__[prop_type], prop_name) if prop_name in links and socket.hide is False: link = links[prop_name] self.id_data.links.new(socket, link['from_socket']) @@ -734,15 +741,22 @@ def setOslProps(self, prop_names, shader_meta): prop_default = float(prop_default) elif prop_type == "int": prop_default = int(float(prop_default)) + elif prop_type == "color": + # error checking on len(prop_default) + # we want len(prop_default) = 4 + if len(prop_default) < 4: + prop_default += (4-len(prop_default)) * (1.0,) + elif len(prop_default) > 4: + prop_default = prop_default[:4] if prop_type == "matrix": - self.inputs.new(rman_socket_utils.__RMAN_SOCKET_MAP__["struct"], prop_name, prop_name) + self.inputs.new(__RMAN_SOCKET_MAP__["struct"], prop_name, prop_name) elif prop_type == "void": pass elif 'lockgeom' in shader_meta[prop_name] and shader_meta[prop_name]['lockgeom'] == 0: pass else: - input = self.inputs.new(rman_socket_utils.__RMAN_SOCKET_MAP__[shader_meta[prop_name]["type"]], + input = self.inputs.new(__RMAN_SOCKET_MAP__[shader_meta[prop_name]["type"]], prop_name, identifier=prop_name) if prop_default: input.default_value = prop_default diff --git a/rman_bl_nodes/rman_bl_nodes_sockets.py b/rman_bl_nodes/rman_bl_nodes_sockets.py index 7ef91535..d909cc35 100644 --- a/rman_bl_nodes/rman_bl_nodes_sockets.py +++ b/rman_bl_nodes/rman_bl_nodes_sockets.py @@ -436,7 +436,7 @@ class RendermanNodeSocketFloat(NodeSocketFloat, RendermanSocket): renderman_type: StringProperty(default='float') is_array: BoolProperty(default=False) array_elem: IntProperty(default=-1) - default_value: IntProperty(update=update_func) + default_value: FloatProperty(update=update_func) @classmethod def draw_color_simple(cls):